/*!
    \file generator_swift.cpp
    \brief Fast binary encoding Swift generator implementation
    \author Andrey Zbranevich
    \ Based on original Fast binary encoding generator design by
    \ Ivan Shynkarenka
    \date 01.08.2019
    \copyright MIT License
*/

#include "generator_swift.h"
#include <iostream>

namespace FBE {

void GeneratorSwift::Generate(const std::shared_ptr<Package>& package)
{
    std::string domain = ConvertDomain(package);

    GenerateFBEPackage(domain, "Fbe");
    GenerateFBEUUIDGenerator(domain, "Fbe");
    GenerateFBEBuffer(domain, "Fbe");
    GenerateFBEModel(domain, "Fbe");
    GenerateFBEFieldModel(domain, "Fbe");
    GenerateFBEFieldModel(domain, "Fbe", "Boolean", "Bool", "", "1", "false");
    GenerateFBEFieldModel(domain, "Fbe", "Byte", "UInt8", "", "1", "0");
    GenerateFBEFieldModel(domain, "Fbe", "Char", "Character", ".utf8.map { UInt8($$0) }[0]", "1", "Character(\"0\")");
    GenerateFBEFieldModel(domain, "Fbe", "WChar", "Character", ".utf16.map { UInt32($$0) }[0]", "4", "Character(\"0\")");
    GenerateFBEFieldModel(domain, "Fbe", "Int8", "Int8", "", "1", "0");
    GenerateFBEFieldModel(domain, "Fbe", "UInt8", "UInt8", "", "1", "0");
    GenerateFBEFieldModel(domain, "Fbe", "Int16", "Int16", "", "2", "0");
    GenerateFBEFieldModel(domain, "Fbe", "UInt16", "UInt16", "", "2", "0");
    GenerateFBEFieldModel(domain, "Fbe", "Int32", "Int32", "", "4", "0");
    GenerateFBEFieldModel(domain, "Fbe", "UInt32", "UInt32", "", "4", "0");
    GenerateFBEFieldModel(domain, "Fbe", "Int64", "Int64", "", "8", "0");
    GenerateFBEFieldModel(domain, "Fbe", "UInt64", "UInt64", "", "8", "0");
    GenerateFBEFieldModel(domain, "Fbe", "Float", "Float", "", "4", "0.0");
    GenerateFBEFieldModel(domain, "Fbe", "Double", "Double", "", "8", "0.0");
    GenerateFBEFieldModel(domain, "Fbe", "UUID", "UUID", "", "16", "UUIDGenerator.nil()");
    GenerateFBEFieldModelDecimal(domain, "Fbe");
    GenerateFBEFieldModelTimestamp(domain, "Fbe");
    GenerateFBEFieldModelBytes(domain, "Fbe");
    GenerateFBEFieldModelString(domain, "Fbe");
    if (Final())
    {
        GenerateFBESize(domain, "Fbe");
        GenerateFBEFinalModel(domain, "Fbe");
        GenerateFBEFinalModel(domain, "Fbe", "Boolean", "Bool", "", "1", "false");
        GenerateFBEFinalModel(domain, "Fbe", "Byte", "UInt8", "", "1", "0");
        GenerateFBEFinalModel(domain, "Fbe", "Char", "Character", ".utf8.map { UInt8($$0) }[0]", "1", "Character(\"0\")");
        GenerateFBEFinalModel(domain, "Fbe", "WChar", "Character", ".utf16.map { UInt32($$0) }[0]", "4", "Character(\"0\")");
        GenerateFBEFinalModel(domain, "Fbe", "Int8", "Int8", "", "1", "0");
        GenerateFBEFinalModel(domain, "Fbe", "UInt8", "UInt8", "", "1", "0");
        GenerateFBEFinalModel(domain, "Fbe", "Int16", "Int16", "", "2", "0");
        GenerateFBEFinalModel(domain, "Fbe", "UInt16", "UInt16", "", "2", "0");
        GenerateFBEFinalModel(domain, "Fbe", "Int32", "Int32", "", "4", "0");
        GenerateFBEFinalModel(domain, "Fbe", "UInt32", "UInt32", "", "4", "0");
        GenerateFBEFinalModel(domain, "Fbe", "Int64", "Int64", "", "8", "0");
        GenerateFBEFinalModel(domain, "Fbe", "UInt64", "UInt64", "", "8", "0");
        GenerateFBEFinalModel(domain, "Fbe", "Float", "Float", "", "4", "0.0");
        GenerateFBEFinalModel(domain, "Fbe", "Double", "Double", "", "8", "0.0");
        GenerateFBEFinalModel(domain, "Fbe", "UUID", "UUID", "", "16", "UUIDGenerator.nil()");
        GenerateFBEFinalModelDecimal(domain, "Fbe");
        GenerateFBEFinalModelTimestamp(domain, "Fbe");
        GenerateFBEFinalModelBytes(domain, "Fbe");
        GenerateFBEFinalModelString(domain, "Fbe");
    }
    if (Proto())
    {
        GenerateFBESender(domain, "Fbe");
        GenerateFBEReceiver(domain, "Fbe");
        GenerateFBEReceiverListener(domain, "Fbe");
        GenerateFBEClient(domain, "Fbe");
    }

    GeneratePackage(package);
}

void GeneratorSwift::GenerateHeader(const std::string& source)
{
    std::string code = R"CODE(// Automatically generated by the Fast Binary Encoding compiler, do not modify!
// https://github.com/chronoxor/FastBinaryEncoding
// Source: _INPUT_
// Version: _VERSION_

)CODE";

    // Prepare code template
    code = std::regex_replace(code, std::regex("_INPUT_"), source);
    code = std::regex_replace(code, std::regex("_VERSION_"), version);
    code = std::regex_replace(code, std::regex("\n"), EndLine());

    Write(code);
}

void GeneratorSwift::GenerateFooter()
{
}

void GeneratorSwift::GenerateImports(const std::string& domain, const std::string& package)
{
    if (!package.empty())
    {
        // Generate package name
        WriteLineIndent("import " + domain + package);
    }
}

void GeneratorSwift::GenerateImports(const std::shared_ptr<Package>& p)
{
    std::string domain = ConvertDomain(p);

    if (p->import)
        for (const auto& i : p->import->imports)
            GenerateImports(domain, ConvertPackageName(*i));
}

void GeneratorSwift::GenerateFBEPackage(const std::string& domain, const std::string& package)
{
    CppCommon::Path path = CppCommon::Path(_output) / CreatePackagePath(domain, package);

    // Create FBE package path
    CppCommon::Directory::CreateTree(path);

    CppCommon::Path packagePath = CppCommon::Path(_output) / package;

    // Create FBE package path
    CppCommon::Directory::CreateTree(packagePath);

    // Generate the file
    CppCommon::Path file = packagePath / "Package.swift";
    WriteBegin();

    std::string code = R"CODE(// swift-tools-version:5.1

import PackageDescription

let package = Package(
    name: "_DOMAIN_",
    products: [
        // Products define the executables and libraries produced by a package, and make them visible to other packages.
        .library(
            name: "_DOMAIN_",
            targets: ["_DOMAIN_"])
    ],
    dependencies: [
        // Dependencies declare other packages that this package depends on.
    ],
    targets: [
        // Targets are the basic building blocks of a package. A target can define a module or a test suite.
        // Targets can depend on other targets in this package, and on products in packages which this package depends on.
        .target(
            name: "_DOMAIN_",
            dependencies: [])
    ]
)
)CODE";

    code = std::regex_replace(code, std::regex("_DOMAIN_"), domain + "Fbe");

    Write(code);

    // Generate footer
    GenerateFooter();

    // Store the file
    WriteEnd();
    Store(file);
}

void GeneratorSwift::GenerateFBEUUIDGenerator(const std::string& domain, const std::string& package)
{
    CppCommon::Path path = CppCommon::Path(_output) / CreatePackagePath(domain, package);

    // Generate the file
    CppCommon::Path file = path / "UUIDGenerator.swift";
    WriteBegin();

    // Generate headers
    GenerateHeader("FBE");
    GenerateImports("", "Foundation");

    std::string code = R"CODE(
// Fast Binary Encoding UUID generator
public struct UUIDGenerator {

    // Generate nil UUID0 (all bits set to zero)
    public static func `nil`() -> UUID {
        return UUID(uuid: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0))
    }

    // Generate sequential UUID1 (time based version)
    public static func sequential() -> UUID {
        var uuid: uuid_t = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
        withUnsafeMutablePointer(to: &uuid) {
            $0.withMemoryRebound(to: UInt8.self, capacity: 16) {
                uuid_generate_time($0)
            }
        }

        return UUID(uuid: uuid)
    }

    // Generate random UUID4 (randomly or pseudo-randomly generated version)
    public static func random() -> UUID {
        var uuid: uuid_t = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
        withUnsafeMutablePointer(to: &uuid) {
            $0.withMemoryRebound(to: UInt8.self, capacity: 16) {
                uuid_generate_random($0)
            }
        }

        return UUID(uuid: uuid)
    }
}

extension UUID: Comparable {
    public static func < (lhs: UUID, rhs: UUID) -> Bool {
        return lhs.hashValue < rhs.hashValue
    }
}
)CODE";

    // Prepare code template
    code = std::regex_replace(code, std::regex("\n"), EndLine());

    Write(code);

    // Generate footer
    GenerateFooter();

    // Store the file
    WriteEnd();
    Store(file);
}

void GeneratorSwift::GenerateFBEBuffer(const std::string& domain, const std::string& package)
{
    CppCommon::Path path = CppCommon::Path(_output) / CreatePackagePath(domain, package);

    // Generate the file
    CppCommon::Path file = path / "Buffer.swift";
    WriteBegin();

    // Generate headers
    GenerateHeader("FBE");
    GenerateImports("", "Foundation");

    std::string code = R"CODE(public class Buffer {

    // Get bytes memory buffer
    public internal(set) var data = Data()

    // Get bytes memory buffer capacity
    public var capacity: Int {
        return data.count
    }

    // Is the buffer empty?
    public var empty: Bool {
        return size == 0
    }

    // Get bytes memory buffer size
    public private(set) var size: Int = 0
    // Get bytes memory buffer offset
    public private(set) var offset: Int = 0

    // Initialize a new expandable buffer with zero capacity
    public init() {}
    // Initialize a new expandable buffer with the given capacity
    public init(capacity: Int) { attach(capacity: capacity) }
    // Initialize a new buffer based on the specified byte array
    public init(buffer: Data) { attach(buffer: buffer) }
    // Initialize a new buffer based on the specified region (offset) of a byte array
    public init(buffer: Data, offset: Int) { attach(buffer: buffer, offset: offset) }
    // Initialize a new buffer based on the specified region (size and offset) of a byte array
    public init(buffer: Data, size: Int, offset: Int) { attach(buffer: buffer, size: size, offset: offset) }

    public func attach() {
        data = Data(capacity: 0)
        size = 0
        offset = 0
    }

    // Attach memory buffer methods
    public func attach(capacity: Int) {
        data = Data(capacity: capacity)
        size = 0
        offset = 0
    }

    public func attach(buffer: Data) {
        data = buffer
        size = buffer.count
        offset = 0
    }

    public func attach(buffer: Data, offset: Int) {
        data = buffer
        size = buffer.count
        self.offset = offset
    }

    public func attach(buffer: Data, size: Int, offset: Int) {
        data = buffer
        self.size = size
        self.offset = offset
    }

    // Allocate memory in the current buffer and return offset to the allocated memory block
    public func allocate(size: Int) throws -> Int {

        if size < 0 {
            throw NSException(name: .invalidArgumentException, reason: "Invalid allocation size!") as! Error
        }

        let offset = self.size

        // Calculate a new buffer size
        let total = Int(self.size + size)

        if total <= data.count {
            self.size = total
            return offset
        }

        var data = Data(count: max(total, 2 * self.size))
        data.withUnsafeMutableBytes { (body: UnsafeMutableRawBufferPointer) in
            if let baseAddress = body.baseAddress, body.count > 0 {
                let pointer = baseAddress.assumingMemoryBound(to: UInt8.self)
                self.data.copyBytes(to: pointer, count: self.data.count)
            }
        }
        self.data = data
        self.size += size

        return offset
    }

    // Remove some memory of the given size from the current buffer
    public func remove(offset: Int, size: Int) throws {
        if offset + size > self.size {
            throw NSException(name: .invalidArgumentException, reason: "Invalid offset & size!") as! Error
        }
        if size != 0 {
            if self.size - size - offset != 0 {
                data[offset...] = data[(offset + size)..<self.size]
            } else {
                data = data[0..<offset]
            }
        }
        self.size -= size
        if self.offset >= offset + size {
            self.offset -= size
        } else if self.offset >= offset {
            self.offset -= self.offset - offset
            if self.offset > self.size {
                self.offset = self.size
            }
        }
    }

    // Reserve memory of the given capacity in the current buffer
    public func reserve(capacity: Int) throws {
        if capacity < 0 {
            throw NSException(name: .invalidArgumentException, reason: "Invalid reserve capacity!") as! Error
        }

        if capacity > data.count {
            var data = Data(capacity: max(capacity, 2 * self.data.count))
            data[0...] = self.data[0...]
            self.data = data
        }
    }

    // Resize the current buffer
    public func resize(size: Int) throws {
        try reserve(capacity: size)

        self.size = size

        if offset > self.size {
            offset = self.size
        }
    }

    // Reset the current buffer and its offset
    public func reset() {
        size = 0
        offset = 0
    }

    // Shift the current buffer offset
    public func shift(offset: Int) { self.offset += offset }
    // Unshift the current buffer offset
    public func unshift(offset: Int) { self.offset -= offset }

    func withDataMutablePointer(offset: Int = 0, block: (UnsafeMutablePointer<UInt8>) -> Void) {
        self.data.withUnsafeMutableBytes { (body: UnsafeMutableRawBufferPointer) in
            if let baseAddress = body.baseAddress, body.count > 0 {
                block(baseAddress.assumingMemoryBound(to: UInt8.self).advanced(by: offset))
            } else {
                assertionFailure()
            }
        }
    }

    func withUnsafeBytes(block: (UnsafeRawPointer) -> Void) {
        self.data.withUnsafeBytes { body in
            if let baseAddress = body.baseAddress, body.count > 0 {
                block(baseAddress)
            } else {
                assertionFailure()
            }
        }
    }

    private func decodeByteNumber<T>(value: inout T, offset: Int, count: Int) {
        withUnsafeMutableBytes(of: &value) { dest -> Void in
            withUnsafeBytes { p in
                let src = p.advanced(by: offset)
                dest.copyMemory(from: UnsafeRawBufferPointer(start: src, count: count))
            }
        }
    }
}
)CODE";

    Write(code);

    code = R"CODE(// MARK: Buffer I/O methods
public extension Buffer {
    class func readBoolean(buffer: Data, offset: Int) -> Bool {
        let index = offset
        return buffer[index] != 0
    }

    class func readByte(buffer: Data, offset: Int) -> UInt8 {
        let index = offset
        return buffer[index]
    }

    class func readChar(buffer: Data, offset: Int) -> Character {
        return Character(UnicodeScalar(Buffer.readUInt8(buffer: buffer, offset: offset)))
    }

    class func readWChar(buffer: Buffer, offset: Int) -> Character {
        return Character(UnicodeScalar(Buffer.readUInt32(buffer: buffer, offset: offset))!)
    }

    class func readInt8(buffer: Buffer, offset: Int) -> Int8 {
        var i: Int8 = 0
        buffer.decodeByteNumber(value: &i, offset: offset, count: 1)
        return Int8(littleEndian: i)
    }

    class func readUInt8(buffer: Data, offset: Int) -> UInt8 {
        let index = offset
        return buffer[index]
    }

    class func readInt16(buffer: Buffer, offset: Int) -> Int16 {
        var i: Int16 = 0
        buffer.decodeByteNumber(value: &i, offset: offset, count: 2)
        return Int16(littleEndian: i)
    }

    class func readUInt16(buffer: Buffer, offset: Int) -> UInt16 {
        var i: UInt16 = 0
        buffer.decodeByteNumber(value: &i, offset: offset, count: 2)
        return UInt16(littleEndian: i)
    }

    class func readInt32(buffer: Buffer, offset: Int) -> Int32 {
        var i: Int32 = 0
        buffer.decodeByteNumber(value: &i, offset: offset, count: 4)
        return Int32(littleEndian: i)
    }

    class func readUInt32(buffer: Buffer, offset: Int) -> UInt32 {
        var i: UInt32 = 0
        buffer.decodeByteNumber(value: &i, offset: offset, count: 4)
        return UInt32(littleEndian: i)
    }

    class func readInt64(buffer: Buffer, offset: Int) -> Int64 {
        var i: Int64 = 0
        buffer.decodeByteNumber(value: &i, offset: offset, count: 8)
        return Int64(littleEndian: i)
    }

    class func readUInt64(buffer: Buffer, offset: Int) -> UInt64 {
        var i: UInt64 = 0
        buffer.decodeByteNumber(value: &i, offset: offset, count: 8)
        return UInt64(littleEndian: i)
    }

    class func readFloat(buffer: Buffer, offset: Int) -> Float {
        let bits = readUInt32(buffer: buffer, offset: offset)
        return Float(bitPattern: bits)
    }

    class func readDouble(buffer: Buffer, offset: Int) -> Double {
        let bitPattern = readUInt64(buffer: buffer, offset: offset)
        return Double(bitPattern: bitPattern)
    }

    class func readBytes(buffer: Data, offset: Int, size: Int) -> Data {
        var result = Data(capacity: size)
        result[0...] = buffer[offset..<(offset + size)]
        return result
    }

    class func readString(buffer: Buffer, offset: Int, size: Int) -> String {
        var value = ""

        buffer.withUnsafeBytes { p in
            let src = p.advanced(by: offset).assumingMemoryBound(to: UInt8.self)
            value = utf8ToString(bytes: src, count: size)
        }
        return value
    }

    class func utf8ToString(bytes: UnsafePointer<UInt8>, count: Int) -> String {
        if count == 0 {
            return String()
        }
        let codeUnits = UnsafeBufferPointer<UInt8>(start: bytes, count: count)
        let sourceEncoding = Unicode.UTF8.self

        //         Verify that the UTF-8 is valid.
        var p = sourceEncoding.ForwardParser()
        var i = codeUnits.makeIterator()
        Loop:
            while true {
                switch p.parseScalar(from: &i) {
                case .valid(_):
                    break
                case .error:
                    return ""
                case .emptyInput:
                    break Loop
                }
        }

        // This initializer is fast but does not reject broken
        // UTF-8 (which is why we validate the UTF-8 above).
        return String(decoding: codeUnits, as: sourceEncoding)
    }

    class func readUUID(buffer: Data, offset: Int) -> UUID {
        let bytes = readBytes(buffer: buffer, offset: offset, size: 16)
        let uuid: uuid_t = (
            bytes[15],
            bytes[14],
            bytes[13],
            bytes[12],
            bytes[11],
            bytes[10],
            bytes[9],
            bytes[8],
            bytes[7],
            bytes[6],
            bytes[5],
            bytes[4],
            bytes[3],
            bytes[2],
            bytes[1],
            bytes[0]
        )

        return UUID(uuid: uuid)
    }

    class func write(buffer: inout Buffer, offset: Int, value: Bool) {
        buffer.data[offset] = value ? 1 : 0
    }

    class func write(buffer: inout Buffer, offset: Int, value: Int8) {
        buffer.withDataMutablePointer(offset: offset) {
            var v = value.littleEndian
            let n = MemoryLayout<UInt8>.size
            memcpy($0, &v, n)
        }
    }

    class func write(buffer: inout Buffer, offset: Int, value: UInt8) {
        buffer.withDataMutablePointer(offset: offset) {
            var v = value.littleEndian
            let n = MemoryLayout<UInt8>.size
            memcpy($0, &v, n)
        }
    }

    class func write(buffer: inout Buffer, offset: Int, value: Int16) {
        buffer.withDataMutablePointer(offset: offset) {
            var v = value.littleEndian
            let n = MemoryLayout<Int16>.size
            memcpy($0, &v, n)
        }
    }

    class func write(buffer: inout Buffer, offset: Int, value: UInt16) {
        buffer.withDataMutablePointer(offset: offset) {
            var v = value.littleEndian
            let n = MemoryLayout<UInt16>.size
            memcpy($0, &v, n)
        }
    }

    class func write(buffer: inout Buffer, offset: Int, value: Int32) {
        buffer.withDataMutablePointer(offset: offset) {
            var v = value.littleEndian
            let n = MemoryLayout<Int32>.size
            memcpy($0, &v, n)
        }
    }

    class func write(buffer: inout Buffer, offset: Int, value: UInt32) {
        buffer.withDataMutablePointer(offset: offset) {
            var v = value.littleEndian
            let n = MemoryLayout<UInt32>.size
            memcpy($0, &v, n)
        }
    }

    class func write(buffer: inout Buffer, offset: Int, value: Int64) {
        buffer.withDataMutablePointer(offset: offset) {
            var v = value.littleEndian
            let n = MemoryLayout<Int64>.size
            memcpy($0, &v, n)
        }
    }

    class func write(buffer: inout Buffer, offset: Int, value: UInt64) {
        buffer.withDataMutablePointer(offset: offset) {
            var v = value.littleEndian
            let n = MemoryLayout<UInt64>.size
            memcpy($0, &v, n)
        }
    }

    class func write(buffer: inout Buffer, offset: Int, value: String) {
        buffer.withDataMutablePointer(offset: offset) {
            var pointer = $0
            var v = UInt32(value.count).littleEndian
            let n = MemoryLayout<UInt32>.size
            memcpy(pointer, &v, n)
            pointer = pointer.advanced(by: n)

            for b in value.utf8 {
                pointer.pointee = b
                pointer = pointer.successor()
            }
        }
    }

    class func write(buffer: inout Buffer, offset: Int, value: Float) {
        let bits = value.bitPattern
        Buffer.write(buffer: &buffer, offset: offset, value: bits)
    }

    class func write(buffer: inout Buffer, offset: Int, value: Double) {
        let bits = value.bitPattern
        Buffer.write(buffer: &buffer, offset: offset, value: bits)
    }

    class func write(buffer: inout Data, offset: Int, value: Data) {
        if value.isEmpty {
            return
        }
        buffer[offset...(offset + value.count - 1)] = value[0...value.count - 1]
    }

    class func write(buffer: inout Buffer, offset: Int, value: Data, valueOffset: Int, valueSize: Int) {
        if valueSize == 0 {
            return
        }

        let values = Array(value[valueOffset..<(valueOffset + valueSize)])
        for i in 0..<values.count {
            Buffer.write(buffer: &buffer, offset: offset + i, value: values[i])
        }
    }

    class func write(buffer: inout Buffer, offset: Int, value: UInt8, valueCount: Int) {
        buffer.withDataMutablePointer(offset: offset) {
            var pointer = $0

            var v = value.littleEndian
            let n = MemoryLayout<UInt8>.size

            for _ in 0..<valueCount {

                memcpy(pointer, &v, n)
                pointer = pointer.successor()
            }
        }
    }

    class func write(buffer: inout Data, offset: Int, value: UUID) {
        Buffer.write(buffer: &buffer, offset: offset, value: Data(Swift.withUnsafeBytes(of: value.uuid, { Data($0) }).reversed()))
    }
}
)CODE";

    // Prepare code template
    code = std::regex_replace(code, std::regex("\n"), EndLine());

    Write(code);

    // Generate footer
    GenerateFooter();

    // Store the file
    WriteEnd();
    Store(file);
}

void GeneratorSwift::GenerateFBEModel(const std::string& domain, const std::string& package)
{
    CppCommon::Path path = CppCommon::Path(_output) / CreatePackagePath(domain, package);

    // Generate the file
    CppCommon::Path file = path / "Model.swift";
    WriteBegin();

    // Generate headers
    GenerateHeader("FBE");
    GenerateImports("", "Foundation");

    std::string code = R"CODE(
// Fast Binary Encoding base model
open class Model {

    // Get bytes buffer
    public private(set) var buffer: Buffer

    // Initialize a new model
    public init(buffer: Buffer = Buffer()) {
        self.buffer = buffer
    }

    // Attach memory buffer methods
    public func attach() { buffer.attach() }
    public func attach(capacity: Int) { buffer.attach(capacity: capacity) }
    public func attach(buffer: Data) { self.buffer.attach(buffer: buffer) }
    public func attach(buffer: Data, offset: Int) { self.buffer.attach(buffer: buffer, offset: offset) }
    public func attach(buffer: Data, size: Int, offset: Int) { self.buffer.attach(buffer: buffer, size: size, offset: offset) }
    public func attach(buffer: Buffer) { self.buffer.attach(buffer: buffer.data, size: buffer.size, offset: buffer.offset) }
    public func attach(buxffer: Buffer, offset: Int) { self.buffer.attach(buffer: buffer.data, size: buffer.size, offset: offset) }

    // Model buffer operations
    public func allocate(size: Int) throws -> Int { return try buffer.allocate(size: size) }
    public func remove(offset: Int, size: Int) throws { try buffer.remove(offset: offset, size: size) }
    public func reserve(capacity: Int) throws { try buffer.reserve(capacity: capacity) }
    public func resize(size: Int) throws { try buffer.resize(size: size) }
    public func reset() { buffer.reset() }
    public func shift(offset: Int) { buffer.shift(offset: offset) }
    public func unshift(offset: Int) { buffer.unshift(offset: offset) }

    // Buffer I/O methods
    public func readUInt32(offset: Int) -> UInt { return UInt(Buffer.readUInt32(buffer: buffer, offset: buffer.offset + offset)) }
    public func write(offset: Int, value: UInt32) { Buffer.write(buffer: &buffer, offset: buffer.offset + offset, value: value) }
}
)CODE";

    // Prepare code template
    code = std::regex_replace(code, std::regex("\n"), EndLine());

    Write(code);

    // Generate footer
    GenerateFooter();

    // Store the file
    WriteEnd();
    Store(file);
}

void GeneratorSwift::GenerateFBEFieldModel(const std::string& domain, const std::string& package)
{
    CppCommon::Path path = CppCommon::Path(_output) / CreatePackagePath(domain, package);

    // Generate the file
    CppCommon::Path file = path / "FieldModel.swift";
    WriteBegin();

    // Generate headers
    GenerateHeader("FBE");
    GenerateImports("", "Foundation");

    std::string code = R"CODE(
// Fast Binary Encoding base field model
public protocol FieldModel: class {
    var _buffer: Buffer { get set }
    var _offset: Int { get set }

    init()
    init(buffer: Buffer, offset: Int)

    var fbeSize: Int { get }
    var fbeExtra: Int { get }
}

public extension FieldModel {

    init(buffer: Buffer, offset: Int) {
        self.init()

        _buffer = buffer
        _offset = offset
    }

    // Field offset
    var fbeOffset: Int {
        get {
            return _offset
        }
        set {
            _offset = newValue
        }
    }

    // Field size
    var fbeSize: Int {
        return 0
    }

    // Field extra size
    var fbeExtra: Int {
        return 0
    }

    // Shift the current field offset
    func fbeShift(size: Int) { _offset += size }
    // Unshift the current field offset
    func fbeUnshift(size: Int) { _offset -= size }

    // Check if the value is valid
    func verify() -> Bool {
        return true
    }

    // Buffer I/O methods
    func readBoolean(offset: Int) -> Bool { return Buffer.readBoolean(buffer: _buffer.data, offset: _buffer.offset + offset) }
    func readByte(offset: Int) -> Data.Element { return Buffer.readByte(buffer: _buffer.data, offset: _buffer.offset + offset) }
    func readChar(offset: Int) -> Character { return Buffer.readChar(buffer: _buffer.data, offset: _buffer.offset + offset) }
    func readWChar(offset: Int) -> Character { return Buffer.readWChar(buffer: _buffer, offset: _buffer.offset + offset) }
    func readInt8(offset: Int) -> Int8 { return Buffer.readInt8(buffer: _buffer, offset: _buffer.offset + offset) }
    func readUInt8(offset: Int) -> UInt8 { return Buffer.readUInt8(buffer: _buffer.data, offset: _buffer.offset + offset) }
    func readInt16(offset: Int) -> Int16 { return Buffer.readInt16(buffer: _buffer, offset: _buffer.offset + offset) }
    func readUInt16(offset: Int) -> UInt16 { return Buffer.readUInt16(buffer: _buffer, offset: _buffer.offset + offset) }
    func readInt32(offset: Int) -> Int32 { return Buffer.readInt32(buffer: _buffer, offset: _buffer.offset + offset) }
    func readUInt32(offset: Int) -> UInt32 { return Buffer.readUInt32(buffer: _buffer, offset: _buffer.offset + offset) }
    func readInt64(offset: Int) -> Int64 { return Buffer.readInt64(buffer: _buffer, offset: _buffer.offset + offset) }
    func readUInt64(offset: Int) -> UInt64 { return Buffer.readUInt64(buffer: _buffer, offset: _buffer.offset + offset) }
    func readFloat(offset: Int) -> Float { return Buffer.readFloat(buffer: _buffer, offset: _buffer.offset + offset) }
    func readDouble(offset: Int) -> Double { return Buffer.readDouble(buffer: _buffer, offset: _buffer.offset + offset) }
    func readBytes(offset: Int, size: Int) -> Data { return Buffer.readBytes(buffer: _buffer.data, offset: _buffer.offset + offset, size: size) }
    func readString(offset: Int, size: Int) -> String { return Buffer.readString(buffer: _buffer, offset: _buffer.offset + offset, size: size) }
    func readUUID(offset: Int) -> UUID { return Buffer.readUUID(buffer: _buffer.data, offset: _buffer.offset + offset) }
    func write(offset: Int, value: Bool) { Buffer.write(buffer: &_buffer, offset: _buffer.offset + offset, value: value) }
    func write(offset: Int, value: Int8) { Buffer.write(buffer: &_buffer, offset: _buffer.offset + offset, value: value) }
    func write(offset: Int, value: UInt8) { Buffer.write(buffer: &_buffer, offset: _buffer.offset + offset, value: value) }
    func write(offset: Int, value: Int16) { Buffer.write(buffer: &_buffer, offset: _buffer.offset + offset, value: value) }
    func write(offset: Int, value: UInt16) { Buffer.write(buffer: &_buffer, offset: _buffer.offset + offset, value: value) }
    func write(offset: Int, value: Int32) { Buffer.write(buffer: &_buffer, offset: _buffer.offset + offset, value: value) }
    func write(offset: Int, value: UInt32) { Buffer.write(buffer: &_buffer, offset: _buffer.offset + offset, value: value) }
    func write(offset: Int, value: Int64) { Buffer.write(buffer: &_buffer, offset: _buffer.offset + offset, value: value) }
    func write(offset: Int, value: UInt64) { Buffer.write(buffer: &_buffer, offset: _buffer.offset + offset, value: value) }
    func write(offset: Int, value: String) { Buffer.write(buffer: &_buffer, offset: _buffer.offset + offset, value: value) }
    func write(offset: Int, value: Float) { Buffer.write(buffer: &_buffer, offset: _buffer.offset + offset, value: value) }
    func write(offset: Int, value: Double) { Buffer.write(buffer: &_buffer, offset: _buffer.offset + offset, value: value) }
    func write(offset: Int, value: Data) { Buffer.write(buffer: &_buffer.data, offset: _buffer.offset + offset, value: value) }
    func write(offset: Int, value: Data, valueOffset: Int, valueSize: Int) { Buffer.write(buffer: &_buffer, offset: _buffer.offset + offset, value: value, valueOffset: valueOffset, valueSize: valueSize) }
    func write(offset: Int, value: Data.Element, valueCount: Int) { Buffer.write(buffer: &_buffer, offset: _buffer.offset + offset, value: value, valueCount: valueCount) }
    func write(offset: Int, value: UUID) { Buffer.write(buffer: &_buffer.data, offset: _buffer.offset + offset, value: value) }
}
)CODE";

    // Prepare code template
    code = std::regex_replace(code, std::regex("\n"), EndLine());

    Write(code);

    // Generate footer
    GenerateFooter();

    // Store the file
    WriteEnd();
    Store(file);
}

void GeneratorSwift::GenerateFBEFieldModel(const std::string& domain, const std::string& package, const std::string& name, const std::string& type, const std::string& base, const std::string& size, const std::string& defaults)
{
    CppCommon::Path path = CppCommon::Path(_output) / CreatePackagePath(domain, package);

    // Generate the file
    CppCommon::Path file = path / ("FieldModel" + name + ".swift");
    WriteBegin();

    // Generate headers
    GenerateHeader("FBE");
    GenerateImports("", "Foundation");

    std::string code = R"CODE(
// Fast Binary Encoding _TYPE_ field model
public class FieldModel_NAME_: FieldModel {
    public var _buffer = Buffer()
    public var _offset: Int = 0

    // Field size
    public let fbeSize: Int = _SIZE_

    public required init() {
        _buffer = Buffer()
        _offset = 0
    }

    public required init(buffer: Buffer, offset: Int) {
        _buffer = buffer
        _offset = offset
    }

    // Get the value
    public func get(defaults: _TYPE_ = _DEFAULTS_) -> _TYPE_ {
        if (_buffer.offset + fbeOffset + fbeSize) > _buffer.size {
            return defaults
        }

        return read_NAME_(offset: fbeOffset)
    }

    // Set the value
    public func set(value: _TYPE_) throws {
        if (_buffer.offset + fbeOffset + fbeSize) > _buffer.size {
            assertionFailure("Model is broken!")
            return
        }

        write(offset: fbeOffset, value: value_BASE_)
    }
}
)CODE";

    // Prepare code template
    code = std::regex_replace(code, std::regex("_NAME_"), name);
    code = std::regex_replace(code, std::regex("_TYPE_"), type);
    code = std::regex_replace(code, std::regex("_SIZE_"), size);
    code = std::regex_replace(code, std::regex("_DEFAULTS_"), defaults);
    code = std::regex_replace(code, std::regex("\n"), EndLine());
    code = std::regex_replace(code, std::regex("_BASE_"), base);

    Write(code);

    // Generate footer
    GenerateFooter();

    // Store the file
    WriteEnd();
    Store(file);
}

void GeneratorSwift::GenerateFBEFieldModelDecimal(const std::string& domain, const std::string& package)
{
    CppCommon::Path path = CppCommon::Path(_output) / CreatePackagePath(domain, package);

    // Generate the file
    CppCommon::Path file = path / "FieldModelDecimal.swift";
    WriteBegin();

    // Generate headers
    GenerateHeader("FBE");
    GenerateImports("", "Foundation");

    std::string code = R"CODE(
// Fast Binary Encoding decimal field model
public class FieldModelDecimal: FieldModel {
    public var _buffer = Buffer()
    public var _offset: Int = 0

    // Field size
    public let fbeSize: Int = 16

    public required init() {
        _buffer = Buffer()
        _offset = 0
    }

    // Get the value
    public func get(defaults: Decimal = Decimal.zero) -> Decimal {
        if (_buffer.offset + fbeOffset + fbeSize) > _buffer.size {
            return defaults
        }

        let lowScaleField = Decimal(string: "18446744073709551616")!
        let midScaleField = Decimal(string: "4294967296")!

        let flags = readUInt32(offset: fbeOffset + 12)
        let negative = (flags & 0x80000000) != 0
        let scale = -Int((flags & 0x7FFFFFFF) >> 16)
        let sign: FloatingPointSign = negative ? .minus : .plus

        var result = Decimal(readUInt32(offset: fbeOffset + 8)) * lowScaleField
        result += Decimal(readUInt32(offset: fbeOffset + 4)) * midScaleField
        result += Decimal(readUInt32(offset: fbeOffset + 0))
        result = Decimal(sign: sign, exponent: scale, significand: result)

        if result.exponent != scale {
            var zero = Decimal(sign: sign, exponent: scale, significand: .zero)
            NSDecimalNormalize(&result, &zero, .up)
        }

        return result
    }

    // Set the value
    public func set(value: Decimal) throws {
        if (_buffer.offset + fbeOffset + fbeSize) > _buffer.size {
            assertionFailure("Model is broken!")
            return
        }

        var valueRef = value
        if valueRef.exponent > 0 {
            // Try to normalize decimal number for .NET Decimal format
            var zero = Decimal.zero
            let error = NSDecimalNormalize(&valueRef, &zero, .up)
            if error != .noError {
                // Issue during normalize decimal number
                write(offset: fbeOffset, value: UInt8.zero, valueCount: fbeSize)
                return
            }
        }

        // Get scale
        let scale = UInt8(abs(valueRef.exponent))
        if scale < 0 || scale > 28 {
            // Value scale exceeds .NET Decimal limit of [0, 28]
            write(offset: fbeOffset, value: UInt8.zero, valueCount: fbeSize)
            return
        }

        // Get byte array
        let unscaledBytes = withUnsafeBytes(of: valueRef) {
            Array($0)
        }

        // Write unscaled value to bytes 0-11
        var index = 0
        let straterIndex = unscaledBytes.count - min(unscaledBytes.count, 16)
        let bytes = Array(unscaledBytes[straterIndex..<min(unscaledBytes.count, 16)])
        while index < 12 {
            write(offset: fbeOffset + index, value: bytes[index])
            index += 1
        }

        // Fill remaining bytes with zeros
        while index < 14 {
            write(offset: fbeOffset + index, value: Int8.zero)
            index += 1
        }

        // Write scale at byte 14
        write(offset: fbeOffset + 14, value: scale)

        // Write signum at byte 15
        write(offset: fbeOffset + 15, value: UInt8(value.isSignMinus ? 128: 0))
    }
}
)CODE";

    // Prepare code template
    code = std::regex_replace(code, std::regex("\n"), EndLine());

    Write(code);

    // Generate footer
    GenerateFooter();

    // Store the file
    WriteEnd();
    Store(file);
}

void GeneratorSwift::GenerateFBEFieldModelTimestamp(const std::string& domain, const std::string& package)
{
    CppCommon::Path path = CppCommon::Path(_output) / CreatePackagePath(domain, package);

    // Generate the file
    CppCommon::Path file = path / "FieldModelTimestamp.swift";
    WriteBegin();

    // Generate headers
    GenerateHeader("FBE");
    GenerateImports("", "Foundation");

    std::string code = R"CODE(
// Fast Binary Encoding timestamp field model
public class FieldModelTimestamp: FieldModel {
    public var _buffer = Buffer()
       public var _offset: Int = 0

       // Field size
       public let fbeSize: Int = 8

       public required init() {
           _buffer = Buffer()
           _offset = 0
       }

    public func get(defaults: Date = Date(timeIntervalSince1970: 0)) -> Date {
        if (_buffer.offset + fbeOffset + fbeSize) > _buffer.size {
            return defaults
        }

        let nanoseconds = TimeInterval(readInt64(offset: fbeOffset))
        return Date(timeIntervalSince1970: nanoseconds / 1_000_000_000)
    }

    public func set(value: Date) throws {
        if (_buffer.offset + fbeOffset + fbeSize) > _buffer.size {
            assertionFailure("Model is broken!")
            return
        }

        let milliseconds = value.timeIntervalSince1970 * 1000
        write(offset: fbeOffset, value: UInt64(milliseconds) * 1_000_000)
    }
}
)CODE";

    // Prepare code template
    code = std::regex_replace(code, std::regex("\n"), EndLine());

    Write(code);

    // Generate footer
    GenerateFooter();

    // Store the file
    WriteEnd();
    Store(file);
}

void GeneratorSwift::GenerateFBEFieldModelBytes(const std::string& domain, const std::string& package)
{
    CppCommon::Path path = CppCommon::Path(_output) / CreatePackagePath(domain, package);

    // Generate the file
    CppCommon::Path file = path / "FieldModelData.swift";
    WriteBegin();

    // Generate headers
    GenerateHeader("FBE");
    GenerateImports("", "Foundation");

    std::string code = R"CODE(
// Fast Binary Encoding bytes field model
public class FieldModelData: FieldModel {
    public var _buffer = Buffer()
    public var _offset: Int = 0

    // Field size
    public let fbeSize: Int = 4

    public required init() {
        _buffer = Buffer()
        _offset = 0
    }

    // Field extra size
    public var fbeExtra: Int {
        if (_buffer.offset + fbeOffset + fbeSize) > _buffer.size {
            return 0
        }

        let fbeBytesOffset = Int(readUInt32(offset: fbeOffset))
        if (fbeBytesOffset == 0) || ((_buffer.offset + fbeBytesOffset + 4) > _buffer.size) {
            return 0
        }

        let fbeBytesSize = Int(readUInt32(offset: fbeBytesOffset))
        return 4 + fbeBytesSize
    }

    public func verify() -> Bool {
        if (_buffer.offset + fbeOffset + fbeSize) > _buffer.size {
            return true
        }

        let fbeBytesOffset = Int(readUInt32(offset: fbeOffset))
        if fbeBytesOffset == 0 {
            return true
        }

        if (_buffer.offset + fbeBytesOffset + 4) > _buffer.size {
            return false
        }

        let fbeBytesSize = Int(readUInt32(offset: fbeBytesOffset))
        if (_buffer.offset + fbeBytesOffset + 4 + fbeBytesSize) > _buffer.size {
            return false
        }

        return true
    }

    // Get the value
    public func get(defaults: Data = Data(count: 0)) -> Data {
        if (_buffer.offset + fbeOffset + fbeSize) > _buffer.size {
            return defaults
        }

        let fbeBytesOffset = Int(readUInt32(offset: fbeOffset))
        if fbeBytesOffset == 0 {
            return defaults
        }

        if _buffer.offset + fbeBytesOffset + 4 > _buffer.size {
            assertionFailure("Model is broken!")
            return defaults
        }

        let fbeBytesSize = Int(readUInt32(offset: fbeBytesOffset))
        if _buffer.offset + fbeBytesOffset + 4 + fbeBytesSize > _buffer.size {
            assertionFailure("Model is broken!")
            return defaults
        }

        return readBytes(offset: fbeBytesOffset + 4, size: fbeBytesSize)
    }

    // Set the value
    public func set(value: Data) throws {
        if _buffer.offset + fbeOffset + fbeSize > _buffer.size {
            assertionFailure("Model is broken!")
            return
        }

        let fbeBytesSize = value.count
        let fbeBytesOffset = try _buffer.allocate(size: 4 + fbeBytesSize) - _buffer.offset
        if fbeBytesOffset <= 0 || (_buffer.offset + fbeBytesOffset + 4 + fbeBytesSize) > _buffer.size {
            assertionFailure("Model is broken!")
            return
        }

        write(offset: fbeOffset, value: UInt32(fbeBytesOffset))
        write(offset: fbeBytesOffset, value: UInt32(fbeBytesSize))
        write(offset: fbeBytesOffset + 4, value: value)
    }
}
)CODE";

    // Prepare code template
    code = std::regex_replace(code, std::regex("\n"), EndLine());

    Write(code);

    // Generate footer
    GenerateFooter();

    // Store the file
    WriteEnd();
    Store(file);
}

void GeneratorSwift::GenerateFBEFieldModelString(const std::string& domain, const std::string& package)
{
    CppCommon::Path path = CppCommon::Path(_output) / CreatePackagePath(domain, package);

    // Generate the file
    CppCommon::Path file = path / "FieldModelString.swift";
    WriteBegin();

    // Generate headers
    GenerateHeader("FBE");

    std::string code = R"CODE(// Fast Binary Encoding string field model
public class FieldModelString: FieldModel {
    public var _buffer: Buffer
    public var _offset: Int

    // Field size
    public let fbeSize: Int = 4

    public required init() {
        _buffer = Buffer()
        _offset = 0
    }

    public required init(buffer: Buffer, offset: Int) {
        _buffer = buffer
        _offset = offset
    }

    public var fbeExtra: Int {
        if (_buffer.offset + fbeOffset + fbeSize) > _buffer.size {
            return 0
        }

        let fbeStringOffset = Int(readUInt32(offset: fbeOffset))
        if (fbeStringOffset == 0) || ((_buffer.offset + fbeStringOffset + 4) > _buffer.size) {
            return 0
        }

        let fbeStringSize = Int(readUInt32(offset: fbeStringOffset))
        return 4 + fbeStringSize
    }

    // Check if the string value is valid
    public func verify() -> Bool {
        if (_buffer.offset + fbeOffset + fbeSize) > _buffer.size {
            return true
        }

        let fbeStringOffset = Int(readUInt32(offset: fbeOffset))
        if fbeStringOffset == 0 {
            return true
        }

        if _buffer.offset + fbeStringOffset + 4 > _buffer.size {
            return false
        }

        let fbeStringSize = Int(readUInt32(offset: fbeStringOffset))
        if (_buffer.offset + fbeStringOffset + 4 + fbeStringSize) > _buffer.size {
            return false
        }

        return true
    }

    // Get the value
    public func get(defaults: String = "") -> String {
        if (_buffer.offset + fbeOffset + fbeSize) > _buffer.size {
            return defaults
        }

        let fbeStringOffset = Int(readUInt32(offset: fbeOffset))
        if fbeStringOffset == 0 {
            return defaults
        }

        if (_buffer.offset + fbeStringOffset + 4) > _buffer.size {
            return defaults
        }

        let fbeStringSize = Int(readUInt32(offset: fbeStringOffset))
        if (_buffer.offset + fbeStringOffset + 4 + fbeStringSize) > _buffer.size {
            return defaults
        }

        return readString(offset: fbeStringOffset + 4, size: fbeStringSize)
    }

    // Set the value
    public func set(value: String) throws {
        if (_buffer.offset + fbeOffset + fbeSize) > _buffer.size {
            return
        }

        let fbeStringSize = value.count
        let fbeStringOffset = try _buffer.allocate(size: 4 + fbeStringSize) - _buffer.offset
        if (fbeStringOffset <= 0) || ((_buffer.offset + fbeStringOffset + 4 + fbeStringSize) > _buffer.size) {
            return
        }

        write(offset: fbeOffset, value: UInt32(fbeStringOffset))
        write(offset: fbeStringOffset, value: value)
    }
}
)CODE";

    // Prepare code template
    code = std::regex_replace(code, std::regex("\n"), EndLine());

    Write(code);

    // Generate footer
    GenerateFooter();

    // Store the file
    WriteEnd();
    Store(file);
}

void GeneratorSwift::GenerateFBEFieldModelOptional(const std::string& domain, const std::shared_ptr<Package>& p, const std::string& name, const std::string& type, const std::string& model)
{
    std::string package = ConvertPackage(p);
    CppCommon::Path path = (CppCommon::Path(_output) / CreatePackagePath(domain, package)) / "Fbe";

    // Create package path
    CppCommon::Directory::CreateTree(path);

    // Generate the file
    CppCommon::Path file = path / ("FieldModelOptional" + name + ".swift");
    WriteBegin();

    // Generate headers
    GenerateHeader(CppCommon::Path(_input).filename().string());
    GenerateImports("", "Foundation");
    GenerateImports(domain, "Fbe");
    GenerateImports(p);

    std::string code = R"CODE(
// Fast Binary Encoding optional _NAME_ field model
public class FieldModelOptional_NAME_: FieldModel {

    public var _buffer: Buffer
    public var _offset: Int

    // Field size
    public let fbeSize: Int = 1 + 4

    // Base field model value
    public let value: _MODEL_

    public var fbeExtra: Int {
        if !hasValue() {
            return 0
        }

        let fbeOptionalOffset = Int(readUInt32(offset: fbeOffset + 1))
        if (fbeOptionalOffset == 0) || ((_buffer.offset + fbeOptionalOffset + 4) > _buffer.size) {
            return 0
        }

        _buffer.shift(offset: fbeOptionalOffset)
        let fbeResult = value.fbeSize + value.fbeExtra
        _buffer.unshift(offset: fbeOptionalOffset)
        return fbeResult
    }

    public required init() {
        let buffer = Buffer()
        let offset = 0

        _buffer = buffer
        _offset = offset

        value = _MODEL_(buffer: buffer, offset: 0)
    }

    public required init(buffer: Buffer, offset: Int) {
        _buffer = buffer
        _offset = offset

        value = _MODEL_(buffer: buffer, offset: 0)
    }

    public func hasValue() -> Bool {
        if (_buffer.offset + fbeOffset + fbeSize) > _buffer.size {
            return false
        }

        let fbeHasValue = Int32(readInt8(offset: fbeOffset))
        return fbeHasValue != 0
    }

    public func verify() -> Bool {
        if _buffer.offset + fbeOffset + fbeSize > _buffer.size {
            return true
        }

        let fbeHasValue = Int(readInt8(offset: fbeOffset))
        if fbeHasValue == 0 {
            return true
        }

        let fbeOptionalOffset = Int(readUInt32(offset: fbeOffset + 1))
        if fbeOptionalOffset == 0 {
            return false
        }

        _buffer.shift(offset: fbeOptionalOffset)
        let fbeResult = value.verify()
        _buffer.unshift(offset: fbeOptionalOffset)
        return fbeResult
    }

    // Get the optional value (being phase)
    func getBegin() -> Int {
        if !hasValue() {
            return 0
        }

        let fbeOptionalOffset = Int(readUInt32(offset: fbeOffset + 1))
        if fbeOptionalOffset <= 0 {
            assertionFailure("Model is broken!")
            return 0
        }

        _buffer.shift(offset: fbeOptionalOffset)
        return fbeOptionalOffset
    }

    // Get the optional value (end phase)
    func getEnd(fbeBegin: Int) {
        _buffer.unshift(offset: fbeBegin)
    }

    public func get(defaults: _TYPE_ = nil) -> _TYPE_ {
        let fbeBegin = getBegin()
        if fbeBegin == 0 {
            return defaults
        }

        let optional = value.get()
        getEnd(fbeBegin: fbeBegin)
        return optional
    }

    // Set the optional value (begin phase)
    func setBegin(hasValue: Bool) throws -> Int {
        if _buffer.offset + fbeOffset + fbeSize > _buffer.size {
            assertionFailure("Model is broken!")
            return 0
        }

        let fbeHasValue = hasValue ? 1 : 0
        write(offset: fbeOffset, value: Int8(fbeHasValue))
        if fbeHasValue == 0 {
            return 0
        }

        let fbeOptionalSize = value.fbeSize
        let fbeOptionalOffset = try _buffer.allocate(size: fbeOptionalSize) - _buffer.offset
        if (fbeOptionalOffset <= 0) || ((_buffer.offset + fbeOptionalOffset + fbeOptionalSize) > _buffer.size) {
            assertionFailure("Model is broken!")
            return 0
        }

        write(offset: fbeOffset + 1, value: UInt32(fbeOptionalOffset))

        _buffer.shift(offset: fbeOptionalOffset)
        return fbeOptionalOffset
    }

    // Set the optional value (end phase)
      func setEnd(fbeBegin: Int) {
        _buffer.unshift(offset: fbeBegin)
      }

      // Set the optional value
      public func set(value optional: _TYPE_) throws {
        let fbeBegin = try setBegin(hasValue: optional != nil)
        if fbeBegin == 0 {
            return
        }

        try value.set(value: optional!)
        setEnd(fbeBegin: fbeBegin)
    }
}
)CODE";

    std::string type_name = type;

    // Prepare code template
    code = std::regex_replace(code, std::regex("_DOMAIN_"), domain);
    code = std::regex_replace(code, std::regex("_NAME_"), name);
    code = std::regex_replace(code, std::regex("_TYPE_"), type_name);
    code = std::regex_replace(code, std::regex("_MODEL_"), model);
    code = std::regex_replace(code, std::regex("\n"), EndLine());

    Write(code);

    // Generate footer
    GenerateFooter();

    // Store the file
    WriteEnd();
    Store(file);
}

void GeneratorSwift::GenerateFBEFieldModelArray(const std::string& domain, const std::shared_ptr<Package>& p, const std::string& name, const std::string& type, const std::string& base, bool optional, const std::string& model)
{
    std::string package = ConvertPackage(p);
    CppCommon::Path path = (CppCommon::Path(_output) / CreatePackagePath(domain, package)) / "Fbe";

    // Create package path
    CppCommon::Directory::CreateTree(path);

    // Generate the file
    CppCommon::Path file = path / ("FieldModelArray" + name + ".swift");
    WriteBegin();

    // Generate headers
    GenerateHeader(CppCommon::Path(_input).filename().string());
    GenerateImports("", "Foundation");
    GenerateImports(domain, "Fbe");
    GenerateImports(p);

    std::string code = R"CODE(
// Fast Binary Encoding _NAME_ array field model
class FieldModelArray_NAME_: FieldModel {
    private let _model: _MODEL_

    var _buffer: Buffer
    var _offset: Int
    var size: Int

    // Field size
    var fbeSize: Int {
        return size * _model.fbeSize
    }

    // Field extra size
    var fbeExtra: Int = 0

    // Get the vector offset
    var offset: Int = 0

    required init() {
        let buffer = Buffer()
        let offset = 0

        _buffer = buffer
        _offset = offset
        self.size = 0

        _model = _MODEL_(buffer: buffer, offset: offset)
    }

    required init(buffer: Buffer, offset: Int, size: Int) {
        _buffer = buffer
        _offset = offset
        self.size =  size

        _model = _MODEL_(buffer: buffer, offset: offset)
    }

    // Vector index operator
    public func getItem(index: Int) -> _MODEL_ {
        assert(_buffer.offset + fbeOffset + fbeSize <= _buffer.size, "Model is broken!")
        assert(index < size, "Model is broken!")

        _model.fbeOffset = fbeOffset
        _model.fbeShift(size: index * _model.fbeSize)
        return _model
    }

    public func verify() -> Bool {
       if _buffer.offset + fbeOffset + fbeSize > _buffer.size {
            return false
        }

        _model.fbeOffset = fbeOffset
        var i = size
        while i > 0 {
            if !_model.verify() { return false }
            _model.fbeShift(size: _model.fbeSize)
            i -= 1
        }

        return true
    }

    public func get() -> _ARRAY_ {
        var values = _ARRAY_()
        let fbeModel = getItem(index: 0)
        for _ in 0..<size {
            values.append(fbeModel.get())
            fbeModel.fbeShift(size: fbeModel.fbeSize)
        }

        return values
    }

    public func get(values: inout _ARRAY_) {
        values.removeAll()

        let fbeVectorSize = size
        if fbeVectorSize == 0 {
            return
        }

        values.reserveCapacity(fbeVectorSize)

        let fbeModel = getItem(index: 0)
        var i = size
        while i > 0 {
            let value = fbeModel.get()
            values.append(value)
            fbeModel.fbeShift(size: fbeModel.fbeSize)
            i -= 1
        }
    }

    public func set(value values: _ARRAY_) throws {
        if _buffer.offset + fbeOffset + fbeSize > _buffer.size {
            assertionFailure("Model is broken!")
            return
        }

        let fbeModel = getItem(index: 0)
        for value in values {
            try fbeModel.set(value: value)
            fbeModel.fbeShift(size: fbeModel.fbeSize)
        }
    }
}
)CODE";

    std::string type_name = IsPackageType(type) ? type : (domain + package + "." + type);

    // Prepare code template
    code = std::regex_replace(code, std::regex("_DOMAIN_"), domain);
    code = std::regex_replace(code, std::regex("_NAME_"), name);
    code = std::regex_replace(code, std::regex("_TYPE_"), type_name);
    code = std::regex_replace(code, std::regex("_MODEL_"), model);

    if (optional)
        code = std::regex_replace(code, std::regex("_ARRAY_"), "Array<" + type_name + ">");
    else
        code = std::regex_replace(code, std::regex("_ARRAY_"), "Array<" + type_name + ">");
    code = std::regex_replace(code, std::regex("\n"), EndLine());

    Write(code);

    // Generate footer
    GenerateFooter();

    // Store the file
    WriteEnd();
    Store(file);
}

void GeneratorSwift::GenerateFBEFieldModelVector(const std::string& domain, const std::shared_ptr<Package>& p, const std::string& name, const std::string& type, const std::string& model)
{
    std::string package = ConvertPackage(p);
    CppCommon::Path path = (CppCommon::Path(_output) / CreatePackagePath(domain, package)) / "Fbe";

    // Create package path
    CppCommon::Directory::CreateTree(path);

    // Generate the file
    CppCommon::Path file = path / ("FieldModelVector" + name + ".swift");
    WriteBegin();

    // Generate headers
    GenerateHeader(CppCommon::Path(_input).filename().string());
    GenerateImports("", "Foundation");
    GenerateImports(domain, "Fbe");
    GenerateImports(p);

    std::string code = R"CODE(
// Fast Binary Encoding _NAME_ vector field model
class FieldModelVector_NAME_: FieldModel {
    private let _model: _MODEL_

    var _buffer: Buffer
    var _offset: Int

    // Field size
    let fbeSize: Int = 4

    var fbeExtra: Int {
        if _buffer.offset + fbeOffset + fbeSize > _buffer.size {
            return 0
        }

        let fbeVectorOffset = Int(readUInt32(offset: fbeOffset))
        if (fbeVectorOffset == 0) || ((_buffer.offset + fbeVectorOffset + 4) > _buffer.size) {
            return 0
        }

        let fbeVectorSize = Int(readUInt32(offset: fbeVectorOffset))

        var fbeResult: Int = 4
        _model.fbeOffset = fbeVectorOffset + 4
        var i = fbeVectorSize
        while i > 0 {
            fbeResult += _model.fbeSize + _model.fbeExtra
            _model.fbeShift(size: _model.fbeSize)
            i -= 1
        }
        return fbeResult
    }

    required init() {
        let buffer = Buffer()
        let offset = 0

        _buffer = buffer
        _offset = offset

        _model = _MODEL_(buffer: buffer, offset: offset)
    }

    required init(buffer: Buffer, offset: Int) {
        _buffer = buffer
        _offset = offset

        _model = _MODEL_(buffer: buffer, offset: offset)
    }

    // Get the vector offset
    var offset: Int {
        if (_buffer.offset + fbeOffset + fbeSize) > _buffer.size {
              return 0
        }

        return Int(readUInt32(offset: fbeOffset))
    }

    // Get the vector offset
    var size: Int {
        if _buffer.offset + fbeOffset + fbeSize > _buffer.size {
            return 0
        }

        let fbeVectorOffset = Int(readUInt32(offset: fbeOffset))
        if (fbeVectorOffset == 0) || ((_buffer.offset + fbeVectorOffset + 4) > _buffer.size) {
            return 0
        }

        return Int(readUInt32(offset: fbeVectorOffset))
    }

    // Vector index operator
    public func getItem(index: Int) -> _MODEL_ {
        assert(_buffer.offset + fbeOffset + fbeSize <= _buffer.size, "Model is broken!")

        let fbeVectorOffset = Int(readUInt32(offset: fbeOffset))
        assert((fbeVectorOffset > 0) && ((_buffer.offset + fbeVectorOffset + 4) <= _buffer.size), "Model is broken!")

        let fbeVectorSize = Int(readUInt32(offset: fbeVectorOffset))
        assert(index < fbeVectorSize, "Index is out of bounds!")

        _model.fbeOffset = fbeVectorOffset + 4
        _model.fbeShift(size: index * _model.fbeSize)
        return _model
    }

    func resize(size: Int) throws -> _MODEL_ {
        let fbeVectorSize = size * _model.fbeSize
        let fbeVectorOffset = try _buffer.allocate(size: 4 + fbeVectorSize) - _buffer.offset
        assert((fbeVectorOffset > 0) && ((_buffer.offset + fbeVectorOffset + 4) <= _buffer.size), "Model is broken!")

        write(offset: fbeOffset, value: UInt32(fbeVectorOffset))
        write(offset: fbeVectorOffset, value: UInt32(size))
        write(offset: fbeVectorOffset + 4, value: UInt8.zero, valueCount: fbeVectorSize)

        _model.fbeOffset = fbeVectorOffset + 4
        return _model
    }

    public func verify() -> Bool {
       if _buffer.offset + fbeOffset + fbeSize > _buffer.size {
            return true
        }

        let fbeVectorOffset = Int(readUInt32(offset: fbeOffset))
        if fbeVectorOffset == 0 {
            return true
        }

        if _buffer.offset + fbeVectorOffset + 4 > _buffer.size {
            return false
        }

        let fbeVectorSize = Int(readUInt32(offset: fbeVectorOffset))
        _model.fbeOffset = fbeVectorOffset + 4
        var i = fbeVectorSize
        while i > 0 {
            if !_model.verify() { return false }
            _model.fbeShift(size: _model.fbeSize)
            i -= 1
        }

        return true
    }

    public func get(values: inout Array<_TYPE_>) {
        values.removeAll()

        let fbeVectorSize = size
        if fbeVectorSize == 0 {
            return
        }

        values.reserveCapacity(fbeVectorSize)

        let fbeModel = getItem(index: 0)
        var i = fbeVectorSize
        while i > 0 {
            let value = fbeModel.get()
            values.append(value)
            fbeModel.fbeShift(size: fbeModel.fbeSize)
            i -= 1
        }
    }

    public func set(value values: Array<_TYPE_>) throws {
        if _buffer.offset + fbeOffset + fbeSize > _buffer.size {
            assertionFailure("Model is broken!")
            return
        }

        let fbeModel = try resize(size: values.count)
        for i in 0..<values.count {
            try fbeModel.set(value: values[i])
            fbeModel.fbeShift(size: fbeModel.fbeSize)
        }
    }
}
)CODE";

    std::string type_name = IsPackageType(type) ? type : (domain + package + "." + type);

    // Prepare code template
    code = std::regex_replace(code, std::regex("_DOMAIN_"), domain);
    code = std::regex_replace(code, std::regex("_NAME_"), name);
    code = std::regex_replace(code, std::regex("_TYPE_"), type_name);
    code = std::regex_replace(code, std::regex("_MODEL_"), model);
    code = std::regex_replace(code, std::regex("\n"), EndLine());

    Write(code);

    // Generate footer
    GenerateFooter();

    // Store the file
    WriteEnd();
    Store(file);
}

void GeneratorSwift::GenerateFBEFieldModelMap(const std::string& domain, const std::shared_ptr<Package>& p, const std::string& key_name, const std::string& key_type, const std::string& key_model, const std::string& value_name, const std::string& value_type, const std::string& value_model)
{
    std::string package = ConvertPackage(p);
    CppCommon::Path path = (CppCommon::Path(_output) / CreatePackagePath(domain, package)) / "Fbe";

    // Create package path
    CppCommon::Directory::CreateTree(path);

    // Generate the file
    CppCommon::Path file = path / ("FieldModelMap" + key_name + value_name + ".swift");
    WriteBegin();

    // Generate headers
    GenerateHeader(CppCommon::Path(_input).filename().string());
    GenerateImports("", "Foundation");
    GenerateImports(domain, "Fbe");
    GenerateImports(p);

    std::string code = R"CODE(
// Fast Binary Encoding _KEY_NAME_->_VALUE_NAME_ map field model
class FieldModelMap_KEY_NAME__VALUE_NAME_: FieldModel {
    private let _modelKey: _KEY_MODEL_
    private let _modelValue: _VALUE_MODEL_

    var _buffer: Buffer
    var _offset: Int

    // Field size
    let fbeSize: Int = 4

    var fbeExtra: Int {
        if _buffer.offset + fbeOffset + fbeSize > _buffer.size {
            return 0
        }

        let fbeMapOffset = Int(readUInt32(offset: fbeOffset))
        if (fbeMapOffset == 0) || ((_buffer.offset + fbeMapOffset + 4) > _buffer.size) {
            return 0
        }

        let fbeMapSize = Int(readUInt32(offset: fbeMapOffset))

        var fbeResult: Int = 4
        _modelKey.fbeOffset = fbeMapOffset + 4
        _modelValue.fbeOffset = fbeMapOffset + 4 + _modelKey.fbeSize
        var i = fbeMapSize
        while i > 0 {
            fbeResult += _modelKey.fbeSize + _modelKey.fbeExtra
            _modelKey.fbeShift(size: _modelKey.fbeSize)
            fbeResult += _modelValue.fbeSize + _modelValue.fbeExtra
            _modelValue.fbeShift(size: _modelValue.fbeSize)
            i -= 1
        }
        return fbeResult
    }

    required init() {
        let buffer = Buffer()
        let offset = 0

        _buffer = buffer
        _offset = offset

        _modelKey = _KEY_MODEL_(buffer: buffer, offset: offset)
        _modelValue = _VALUE_MODEL_(buffer: buffer, offset: offset)
    }

    required init(buffer: Buffer, offset: Int) {
        _buffer = buffer
        _offset = offset

        _modelKey = _KEY_MODEL_(buffer: buffer, offset: offset)
        _modelValue = _VALUE_MODEL_(buffer: buffer, offset: offset)
    }

    // Get the vector offset
    var offset: Int {
        if (_buffer.offset + fbeOffset + fbeSize) > _buffer.size {
              return 0
        }

        return Int(readUInt32(offset: fbeOffset))
    }

    // Get the vector offset
    var size: Int {
        if _buffer.offset + fbeOffset + fbeSize > _buffer.size {
            return 0
        }

        let fbeMapOffset = Int(readUInt32(offset: fbeOffset))
        if (fbeMapOffset == 0) || ((_buffer.offset + fbeMapOffset + 4) > _buffer.size) {
            return 0
        }

        return Int(readUInt32(offset: fbeMapOffset))
    }

    // Vector index operator
    public func getItem(index: Int) -> (_KEY_MODEL_, _VALUE_MODEL_) {
        assert(_buffer.offset + fbeOffset + fbeSize <= _buffer.size, "Model is broken!")

        let fbeMapOffset = Int(readUInt32(offset: fbeOffset))
        assert((fbeMapOffset > 0) && ((_buffer.offset + fbeMapOffset + 4) <= _buffer.size), "Model is broken!")

        let fbeMapSize = Int(readUInt32(offset: fbeMapOffset))
        assert(index < fbeMapSize, "Index is out of bounds!")

        _modelKey.fbeOffset = fbeMapOffset + 4
        _modelValue.fbeOffset = fbeMapOffset + 4 + _modelKey.fbeSize
        _modelKey.fbeShift(size: index * (_modelKey.fbeSize + _modelValue.fbeSize))
        _modelValue.fbeShift(size: index * (_modelKey.fbeSize + _modelValue.fbeSize))
        return (_modelKey, _modelValue)
    }

    func resize(size: Int) throws -> (_KEY_MODEL_, _VALUE_MODEL_) {
        let fbeMapSize = size * (_modelKey.fbeSize + _modelValue.fbeSize)
        let fbeMapOffset = try _buffer.allocate(size: 4 + fbeMapSize) - _buffer.offset
        assert((fbeMapOffset > 0) && ((_buffer.offset + fbeMapOffset + 4) <= _buffer.size), "Model is broken!")

        write(offset: fbeOffset, value: UInt32(fbeMapOffset))
        write(offset: fbeMapOffset, value: UInt32(size))
        write(offset: fbeMapOffset + 4, value: UInt8.zero, valueCount: fbeMapSize)

        _modelKey.fbeOffset = fbeMapOffset + 4
        _modelValue.fbeOffset = fbeMapOffset + 4 + _modelKey.fbeSize
        return (_modelKey, _modelValue)
    }

    public func verify() -> Bool {
       if _buffer.offset + fbeOffset + fbeSize > _buffer.size {
            return true
        }

        let fbeMapOffset = Int(readUInt32(offset: fbeOffset))
        if fbeMapOffset == 0 {
            return true
        }

        if _buffer.offset + fbeMapOffset + 4 > _buffer.size {
            return false
        }

        let fbeMapSize = Int(readUInt32(offset: fbeMapOffset))
        _modelKey.fbeOffset = fbeMapOffset + 4
        _modelValue.fbeOffset = fbeMapOffset + 4 + _modelKey.fbeSize
        var i = fbeMapSize
        while i > 0 {
            if !_modelKey.verify() { return false }
            _modelKey.fbeShift(size: _modelKey.fbeSize + _modelValue.fbeSize)
            if !_modelValue.verify() { return false }
            _modelValue.fbeShift(size: _modelKey.fbeSize + _modelValue.fbeSize)
            i -= 1
        }

        return true
    }

    public func get(values: inout Dictionary<_KEY_TYPE_, _VALUE_TYPE_>) {
        values.removeAll()

        let fbeMapSize = size
        if fbeMapSize == 0 {
            return
        }

        values.reserveCapacity(fbeMapSize)

        let fbeModel = getItem(index: 0)
        var i = fbeMapSize
        while i > 0 {
            let key = fbeModel.0.get()
            let value = fbeModel.1.get()
            values[key] = value
            fbeModel.0.fbeShift(size: fbeModel.0.fbeSize + fbeModel.1.fbeSize)
            fbeModel.1.fbeShift(size: fbeModel.0.fbeSize + fbeModel.1.fbeSize)
            i -= 1
        }
    }

    public func set(value values: Dictionary<_KEY_TYPE_, _VALUE_TYPE_>) throws {
        if _buffer.offset + fbeOffset + fbeSize > _buffer.size {
            assertionFailure("Model is broken!")
            return
        }

        let fbeModel = try resize(size: values.count)
        for (key, value) in values {
            try fbeModel.0.set(value: key)
            fbeModel.0.fbeShift(size: fbeModel.0.fbeSize + fbeModel.1.fbeSize)
            try fbeModel.1.set(value: value)
            fbeModel.1.fbeShift(size: fbeModel.0.fbeSize + fbeModel.1.fbeSize)
        }
    }
}
)CODE";

  std::string key_type_name = IsPackageType(key_type) ? key_type : (domain + package + "." + key_type);
  std::string value_type_name = IsPackageType(value_type) ? value_type : (domain + package + "." + value_type);

    // Prepare code template
    code = std::regex_replace(code, std::regex("_DOMAIN_"), domain);
    code = std::regex_replace(code, std::regex("_KEY_NAME_"), key_name);
    code = std::regex_replace(code, std::regex("_KEY_TYPE_"), key_type_name);
    code = std::regex_replace(code, std::regex("_KEY_MODEL_"), key_model);
    code = std::regex_replace(code, std::regex("_VALUE_NAME_"), value_name);
    code = std::regex_replace(code, std::regex("_VALUE_TYPE_"), value_type_name);
    code = std::regex_replace(code, std::regex("_VALUE_MODEL_"), value_model);
    code = std::regex_replace(code, std::regex("\n"), EndLine());

    Write(code);

    // Generate footer
    GenerateFooter();

    // Store the file
    WriteEnd();
    Store(file);
}

void GeneratorSwift::GenerateFBEFieldModelEnumFlags(const std::string& domain, const std::string& package, const std::string& name, const std::string& type)
{
    CppCommon::Path path = (CppCommon::Path(_output) / CreatePackagePath(domain, package)) / "Fbe";

    // Create package path
    CppCommon::Directory::CreateTree(path);

    // Generate the file
    CppCommon::Path file = path / ("FieldModel" + name + ".swift");
    WriteBegin();

    // Generate headers
    GenerateHeader(CppCommon::Path(_input).filename().string());
    GenerateImports(domain, "Fbe");

    std::string code = R"CODE(
// Fast Binary Encoding _NAME_ field model
public class FieldModel_NAME_: FieldModel {

    public var _buffer: Buffer = Buffer()
    public var _offset: Int = 0

    public var fbeSize: Int = _SIZE_

    public required init() {
        _buffer = Buffer()
        _offset = 0
    }

    // Get the value
    public func get(defaults: _NAME_ = _NAME_()) -> _NAME_ {
        if _buffer.offset + fbeOffset + fbeSize > _buffer.size {
            return defaults
        }

        return _NAME_(value: _READ_(offset: fbeOffset))
    }

    // Set the value
    public func set(value: _NAME_) throws {
        if (_buffer.offset + fbeOffset + fbeSize) > _buffer.size {
            assertionFailure("Model is broken!")
            return
        }

        write(offset: fbeOffset, value: value.raw)
    }
}
)CODE";

    // Prepare code template
    code = std::regex_replace(code, std::regex("_DOMAIN_"), domain);
    code = std::regex_replace(code, std::regex("_PACKAGE_"), package);
    code = std::regex_replace(code, std::regex("_NAME_"), name);
    code = std::regex_replace(code, std::regex("_SIZE_"), ConvertEnumSize(type));
    code = std::regex_replace(code, std::regex("_READ_"), ConvertEnumRead(type));
    code = std::regex_replace(code, std::regex("\n"), EndLine());

    Write(code);

    // Generate footer
    GenerateFooter();

    // Store the file
    WriteEnd();
    Store(file);
}

void GeneratorSwift::GenerateFBESize(const std::string& domain, const std::string& package)
{
    CppCommon::Path path = CppCommon::Path(_output) / CreatePackagePath(domain, package);

    // Generate the file
    CppCommon::Path file = path / "Size.swift";
    WriteBegin();

    // Generate headers
    GenerateHeader("FBE");

    std::string code = R"CODE(
// Fast Binary Encoding size
public class Size {
    public var value: Int

    public init(size: Int = 0) {
        value = size
    }
}
)CODE";

    // Prepare code template
    code = std::regex_replace(code, std::regex("\n"), EndLine());

    Write(code);

    // Generate footer
    GenerateFooter();

    // Store the file
    WriteEnd();
    Store(file);
}

void GeneratorSwift::GenerateFBEFinalModel(const std::string& domain, const std::string& package)
{
    CppCommon::Path path = CppCommon::Path(_output) / CreatePackagePath(domain, package);

    // Generate the file
    CppCommon::Path file = path / "FinalModel.swift";
    WriteBegin();

    // Generate headers
    GenerateHeader("FBE");
    GenerateImports("", "Foundation");

    std::string code = R"CODE(
// Fast Binary Encoding base final model
public protocol FinalModel: class {
    var _buffer: Buffer { get set }
    var _offset: Int { get set }

    // Field size
    var fbeSize: Int { get }
    // Field extra size
    var fbeExtra: Int { get }
}

public extension FinalModel {

    // Field offset
    var fbeOffset: Int {
        get {
            return _offset
        }
        set {
            _offset = newValue
        }
    }

    // Field size
    var fbeSize: Int {
        return 0
    }

    // Field extra size
    var fbeExtra: Int {
        return 0
    }

    // Shift the current field offset
    func fbeShift(size: Int) { _offset += size }
    // Unshift the current field offset
    func fbeUnshift(size: Int) { _offset -= size }

    // Check if the value is valid
    func verify() -> Bool {
        return true
    }

    // Buffer I/O methods
    func readBoolean(offset: Int) -> Bool { return Buffer.readBoolean(buffer: _buffer.data, offset: _buffer.offset + offset) }
    func readByte(offset: Int) -> Data.Element { return Buffer.readByte(buffer: _buffer.data, offset: _buffer.offset + offset) }
    func readChar(offset: Int) -> Character { return Buffer.readChar(buffer: _buffer.data, offset: _buffer.offset + offset) }
    func readWChar(offset: Int) -> Character { return Buffer.readWChar(buffer: _buffer, offset: _buffer.offset + offset) }
    func readInt8(offset: Int) -> Int8 { return Buffer.readInt8(buffer: _buffer, offset: _buffer.offset + offset) }
    func readUInt8(offset: Int) -> UInt8 { return Buffer.readUInt8(buffer: _buffer.data, offset: _buffer.offset + offset) }
    func readInt16(offset: Int) -> Int16 { return Buffer.readInt16(buffer: _buffer, offset: _buffer.offset + offset) }
    func readUInt16(offset: Int) -> UInt16 { return Buffer.readUInt16(buffer: _buffer, offset: _buffer.offset + offset) }
    func readInt32(offset: Int) -> Int32 { return Buffer.readInt32(buffer: _buffer, offset: _buffer.offset + offset) }
    func readUInt32(offset: Int) -> UInt32 { return Buffer.readUInt32(buffer: _buffer, offset: _buffer.offset + offset) }
    func readInt64(offset: Int) -> Int64 { return Buffer.readInt64(buffer: _buffer, offset: _buffer.offset + offset) }
    func readUInt64(offset: Int) -> UInt64 { return Buffer.readUInt64(buffer: _buffer, offset: _buffer.offset + offset) }
    func readFloat(offset: Int) -> Float { return Buffer.readFloat(buffer: _buffer, offset: _buffer.offset + offset) }
    func readDouble(offset: Int) -> Double { return Buffer.readDouble(buffer: _buffer, offset: _buffer.offset + offset) }
    func readBytes(offset: Int, size: Int) -> Data { return Buffer.readBytes(buffer: _buffer.data, offset: _buffer.offset + offset, size: size) }
    func readString(offset: Int, size: Int) -> String { return Buffer.readString(buffer: _buffer, offset: _buffer.offset + offset, size: size) }
    func readUUID(offset: Int) -> UUID { return Buffer.readUUID(buffer: _buffer.data, offset: _buffer.offset + offset) }
    func write(offset: Int, value: Bool) { Buffer.write(buffer: &_buffer, offset: _buffer.offset + offset, value: value) }
    func write(offset: Int, value: Int8) { Buffer.write(buffer: &_buffer, offset: _buffer.offset + offset, value: value) }
    func write(offset: Int, value: UInt8) { Buffer.write(buffer: &_buffer, offset: _buffer.offset + offset, value: value) }
    func write(offset: Int, value: Int16) { Buffer.write(buffer: &_buffer, offset: _buffer.offset + offset, value: value) }
    func write(offset: Int, value: UInt16) { Buffer.write(buffer: &_buffer, offset: _buffer.offset + offset, value: value) }
    func write(offset: Int, value: Int32) { Buffer.write(buffer: &_buffer, offset: _buffer.offset + offset, value: value) }
    func write(offset: Int, value: UInt32) { Buffer.write(buffer: &_buffer, offset: _buffer.offset + offset, value: value) }
    func write(offset: Int, value: Int64) { Buffer.write(buffer: &_buffer, offset: _buffer.offset + offset, value: value) }
    func write(offset: Int, value: UInt64) { Buffer.write(buffer: &_buffer, offset: _buffer.offset + offset, value: value) }
    func write(offset: Int, value: String) { Buffer.write(buffer: &_buffer, offset: _buffer.offset + offset, value: value) }
    func write(offset: Int, value: Float) { Buffer.write(buffer: &_buffer, offset: _buffer.offset + offset, value: value) }
    func write(offset: Int, value: Double) { Buffer.write(buffer: &_buffer, offset: _buffer.offset + offset, value: value) }
    func write(offset: Int, value: Data) { Buffer.write(buffer: &_buffer.data, offset: _buffer.offset + offset, value: value) }
    func write(offset: Int, value: Data, valueOffset: Int, valueSize: Int) { Buffer.write(buffer: &_buffer, offset: _buffer.offset + offset, value: value, valueOffset: valueOffset, valueSize: valueSize) }
    func write(offset: Int, value: Data.Element, valueCount: Int) { Buffer.write(buffer: &_buffer, offset: _buffer.offset + offset, value: value, valueCount: valueCount) }
    func write(offset: Int, value: UUID) { Buffer.write(buffer: &_buffer.data, offset: _buffer.offset + offset, value: value) }
}
)CODE";

    // Prepare code template
    code = std::regex_replace(code, std::regex("\n"), EndLine());

    Write(code);

    // Generate footer
    GenerateFooter();

    // Store the file
    WriteEnd();
    Store(file);
}

void GeneratorSwift::GenerateFBEFinalModel(const std::string& domain, const std::string& package, const std::string& name, const std::string& type, const std::string& base, const std::string& size, const std::string& defaults)
{
    CppCommon::Path path = CppCommon::Path(_output) / CreatePackagePath(domain, package);

    // Generate the file
    CppCommon::Path file = path / ("FinalModel" + name + ".swift");
    WriteBegin();

    // Generate headers
    GenerateHeader("FBE");
    GenerateImports("", "Foundation");

    std::string code = R"CODE(
// Fast Binary Encoding _TYPE_ final model
public class FinalModel_NAME_: FinalModel {
    public var _buffer = Buffer()
    public var _offset: Int = 0

    public init(buffer: Buffer, offset: Int) {
        _buffer = buffer
        _offset = offset
    }

    // Get the allocation size
    public func fbeAllocationSize(value: _TYPE_) -> Int {
        return fbeSize
    }

    // Field size
    public let fbeSize: Int = _SIZE_

    // Check if the value is valid
    public func verify() -> Int {
        if _buffer.offset + fbeOffset + fbeSize > _buffer.size {
            return Int.max
        }

        return fbeSize
    }

    // Get the value
    public func get(size: inout Size) -> _TYPE_ {
        if (_buffer.offset + fbeOffset + fbeSize) > _buffer.size {
            return _DEFAULTS_
        }

        size.value = fbeSize
        return read_NAME_(offset: fbeOffset)
    }

    // Set the value
    public func set(value: _TYPE_) throws -> Int {
        if (_buffer.offset + fbeOffset + fbeSize) > _buffer.size {
            assertionFailure("Model is broken!")
            return 0
        }

        write(offset: fbeOffset, value: value_BASE_)
        return fbeSize
    }
}
)CODE";

    // Prepare code template
    code = std::regex_replace(code, std::regex("_NAME_"), name);
    code = std::regex_replace(code, std::regex("_TYPE_"), type);
    code = std::regex_replace(code, std::regex("_BASE_"), base);
    code = std::regex_replace(code, std::regex("_SIZE_"), size);
    code = std::regex_replace(code, std::regex("_DEFAULTS_"), defaults);
    code = std::regex_replace(code, std::regex("\n"), EndLine());

    Write(code);

    // Generate footer
    GenerateFooter();

    // Store the file
    WriteEnd();
    Store(file);
}

void GeneratorSwift::GenerateFBEFinalModelDecimal(const std::string& domain, const std::string& package)
{
    CppCommon::Path path = CppCommon::Path(_output) / CreatePackagePath(domain, package);

    // Generate the file
    CppCommon::Path file = path / "FinalModelDecimal.swift";
    WriteBegin();

    // Generate headers
    GenerateHeader("FBE");
    GenerateImports("", "Foundation");

    std::string code = R"CODE(
// Fast Binary Encoding decimal final model
public class FinalModelDecimal: FinalModel {
    public var _buffer = Buffer()
    public var _offset: Int = 0

    // Field size
    public let fbeSize: Int = 16

    public init(buffer: Buffer, offset: Int) {
        _buffer = buffer
        _offset = offset
    }

    public func fbeAllocationSize(value: Decimal) -> Int {
        return fbeSize
    }

    public func verify() -> Int {
        if (_buffer.offset + fbeOffset + fbeSize) > _buffer.size {
            return Int.max
        }

        return fbeSize
    }

    // Get the value
    public func get(size: inout Size) -> Decimal {
        if (_buffer.offset + fbeOffset + fbeSize) > _buffer.size {
            return Decimal.zero
        }

        let lowScaleField = Decimal(string: "18446744073709551616")!
        let midScaleField = Decimal(string: "4294967296")!

        let flags = readUInt32(offset: fbeOffset + 12)
        let negative = (flags & 0x80000000) != 0
        let scale = -Int((flags & 0x7FFFFFFF) >> 16)
        let sign: FloatingPointSign = negative ? .minus : .plus

        var result = Decimal(readUInt32(offset: fbeOffset + 8)) * lowScaleField
        result += Decimal(readUInt32(offset: fbeOffset + 4)) * midScaleField
        result += Decimal(readUInt32(offset: fbeOffset + 0))
        result = Decimal(sign: sign, exponent: scale, significand: result)

        if result.exponent != scale {
            var zero = Decimal(sign: sign, exponent: scale, significand: .zero)
            NSDecimalNormalize(&result, &zero, .up)
        }

        size.value = fbeSize
        return result
    }

    // Set the value
    public func set(value: Decimal) throws -> Int {
        if (_buffer.offset + fbeOffset + fbeSize) > _buffer.size {
            assertionFailure("Model is broken!")
            return 0
        }

        var valueRef = value
        if valueRef.exponent > 0 {
            // Try to normalize decimal number for .NET Decimal format
            var zero = Decimal.zero
            let error = NSDecimalNormalize(&valueRef, &zero, .up)
            if error != .noError {
                // Issue during normalize decimal number
                write(offset: fbeOffset, value: UInt8.zero, valueCount: fbeSize)
                return 0
            }
        }

        // Get scale
        let scale = UInt8(abs(valueRef.exponent))
        if scale < 0 || scale > 28 {
            // Value scale exceeds .NET Decimal limit of [0, 28]
            write(offset: fbeOffset, value: UInt8.zero, valueCount: fbeSize)
            return 0
        }

        // Get byte array
        let unscaledBytes = withUnsafeBytes(of: valueRef) {
            Array($0)
        }

        // Write unscaled value to bytes 0-11
        var index = 0
        let straterIndex = unscaledBytes.count - min(unscaledBytes.count, 16)
        let bytes = Array(unscaledBytes[straterIndex..<min(unscaledBytes.count, 16)])
        while index < 12 {
            write(offset: fbeOffset + index, value: bytes[index])
            index += 1
        }

        // Fill remaining bytes with zeros
        while index < 14 {
            write(offset: fbeOffset + index, value: Int8.zero)
            index += 1
        }

        // Write scale at byte 14
        write(offset: fbeOffset + 14, value: scale)

        // Write signum at byte 15
        write(offset: fbeOffset + 15, value: UInt8(value.isSignMinus ? 128: 0))
        return fbeSize
    }
}
)CODE";

    // Prepare code template
    code = std::regex_replace(code, std::regex("\n"), EndLine());

    Write(code);

    // Generate footer
    GenerateFooter();

    // Store the file
    WriteEnd();
    Store(file);
}

void GeneratorSwift::GenerateFBEFinalModelTimestamp(const std::string& domain, const std::string& package)
{
    CppCommon::Path path = CppCommon::Path(_output) / CreatePackagePath(domain, package);

    // Generate the file
    CppCommon::Path file = path / "FinalModelTimestamp.swift";
    WriteBegin();

    // Generate headers
    GenerateHeader("FBE");
    GenerateImports("", "Foundation");

    std::string code = R"CODE(
// Fast Binary Encoding timestamp final model
public class FinalModelTimestamp: FinalModel {
    public var _buffer = Buffer()
    public var _offset: Int = 0

    public init(buffer: Buffer, offset: Int) {
        _buffer = buffer
        _offset = offset
    }

    // Get the allocation size
    public func fbeAllocationSize(value: Date) -> Int {
        return fbeSize
    }

    // Field size
    public let fbeSize: Int = 8

    // Check if the value is valid
    public func verify() -> Int {
        if _buffer.offset + fbeOffset + fbeSize > _buffer.size {
            return Int.max
        }

        return fbeSize
    }

    // Get the value
    public func get(size: inout Size) -> Date {
        if _buffer.offset + fbeOffset + fbeSize > _buffer.size {
            return Date(timeIntervalSince1970: 0)
        }

        size.value = fbeSize
        let nanoseconds = TimeInterval(readInt64(offset: fbeOffset))
        return Date(timeIntervalSince1970: nanoseconds / 1_000_000_000)
    }

    // Set the value
    public func set(value: Date) throws -> Int {
        if _buffer.offset + fbeOffset + fbeSize > _buffer.size {
            assertionFailure("Model is broken!")
            return 0
        }

        let milliseconds = value.timeIntervalSince1970 * 1000
        write(offset: fbeOffset, value: UInt64(milliseconds) * 1_000_000)
        return fbeSize
    }
}
)CODE";

    // Prepare code template
    code = std::regex_replace(code, std::regex("\n"), EndLine());

    Write(code);

    // Generate footer
    GenerateFooter();

    // Store the file
    WriteEnd();
    Store(file);
}

void GeneratorSwift::GenerateFBEFinalModelBytes(const std::string& domain, const std::string& package)
{
    CppCommon::Path path = CppCommon::Path(_output) / CreatePackagePath(domain, package);

    // Generate the file
    CppCommon::Path file = path / "FinalModelData.swift";
    WriteBegin();

    // Generate headers
    GenerateHeader("FBE");
    GenerateImports("", "Foundation");

    std::string code = R"CODE(
// Fast Binary Encoding bytes final model
public class FinalModelData: FinalModel {
    public var _buffer = Buffer()
    public var _offset: Int = 0

    public init(buffer: Buffer, offset: Int) {
        _buffer = buffer
        _offset = offset
    }

    public func fbeAllocationSize(value: Data) -> Int {
        return 4 + value.count
    }

    public func verify() -> Int {
        if (_buffer.offset + fbeOffset) + 4 > _buffer.size {
            return Int.max
        }

        let fbeBytesSize = Int(readUInt32(offset: fbeOffset))
        if (_buffer.offset + fbeOffset + 4 + fbeBytesSize) > _buffer.size {
            return Int.max
        }

        return 4 + fbeBytesSize
    }

    // Get the value
    public func get(size: inout Size) -> Data {
        if (_buffer.offset + fbeOffset + fbeSize) > _buffer.size {
            size.value = 0
            return Data()
        }

        let fbeBytesSize = Int(readUInt32(offset: fbeOffset))
        if _buffer.offset + fbeOffset + 4 + fbeBytesSize > _buffer.size {
            assertionFailure("Model is broken!")
            size.value = 4
            return Data()
        }

        size.value = 4 + fbeBytesSize
        return readBytes(offset: fbeOffset + 4, size: fbeBytesSize)
    }

    // Set the value
    public func set(value: Data) throws -> Int {
        if (_buffer.offset + fbeOffset + 4) > _buffer.size {
            assertionFailure("Model is broken!")
            return 0
        }

        let fbeBytesSize = value.count
        if _buffer.offset + fbeOffset + 4 + fbeBytesSize > _buffer.size {
            assertionFailure("Model is broken!")
            return 4
        }

        write(offset: fbeOffset, value: UInt32(fbeBytesSize))
        write(offset: fbeOffset + 4, value: value)
        return 4 + fbeBytesSize
    }
}
)CODE";

    // Prepare code template
    code = std::regex_replace(code, std::regex("\n"), EndLine());

    Write(code);

    // Generate footer
    GenerateFooter();

    // Store the file
    WriteEnd();
    Store(file);
}

void GeneratorSwift::GenerateFBEFinalModelString(const std::string& domain, const std::string& package)
{
    CppCommon::Path path = CppCommon::Path(_output) / CreatePackagePath(domain, package);

    // Generate the file
    CppCommon::Path file = path / "FinalModelString.swift";
    WriteBegin();

    // Generate headers
    GenerateHeader("FBE");

    std::string code = R"CODE(
// Fast Binary Encoding string final model
public class FinalModelString: FinalModel {
    public var _buffer = Buffer()
    public var _offset: Int = 0

    public init(buffer: Buffer, offset: Int) {
        _buffer = buffer
        _offset = offset
    }

    public func fbeAllocationSize(value: String) -> Int {
        return 4 + 3 * (value.count + 1)
    }

    // Check if the value is valid
    public func verify() -> Int {
        if _buffer.offset + fbeOffset + 4 > _buffer.size {
            return Int.max
        }

        let fbeStringSize = Int(readUInt32(offset: fbeOffset))
        if _buffer.offset + fbeOffset + 4 + fbeStringSize > _buffer.size {
            return Int.max
        }

        return 4 + fbeStringSize
    }

    // Get the value
    public func get(size: inout Size) -> String {
        if _buffer.offset + fbeOffset + 4 > _buffer.size {
            size.value = 0
            return ""
        }

        let fbeStringSize = Int(readUInt32(offset: fbeOffset))
        if _buffer.offset + fbeOffset + 4 + fbeStringSize > _buffer.size {
            size.value = 4
            return ""
        }

        size.value = 4 + fbeStringSize
        return readString(offset: fbeOffset + 4, size: fbeStringSize)
    }

    // Set the value
    public func set(value: String) throws -> Int {
        if _buffer.offset + fbeOffset + 4 > _buffer.size {
            return 0
        }

        let fbeStringSize = value.count
        if _buffer.offset + fbeOffset + 4 + fbeStringSize > _buffer.size {
            return 4
        }

        write(offset: fbeOffset, value: value)
        return 4 + fbeStringSize
    }
}
)CODE";

    // Prepare code template
    code = std::regex_replace(code, std::regex("\n"), EndLine());

    Write(code);

    // Generate footer
    GenerateFooter();

    // Store the file
    WriteEnd();
    Store(file);
}

void GeneratorSwift::GenerateFBEFinalModelOptional(const std::string& domain, const std::shared_ptr<Package>& p, const std::string& name, const std::string& type, const std::string& model)
{
    std::string package = ConvertPackage(p);
    CppCommon::Path path = (CppCommon::Path(_output) / CreatePackagePath(domain, package)) / "Fbe";

    // Create package path
    CppCommon::Directory::CreateTree(path);

    // Generate the file
    CppCommon::Path file = path / ("FinalModelOptional" + name + ".swift");
    WriteBegin();

    // Generate headers
    GenerateHeader(CppCommon::Path(_input).filename().string());
    GenerateImports("", "Foundation");
    GenerateImports(domain, "Fbe");
    GenerateImports(p);

    std::string code = R"CODE(
// Fast Binary Encoding optional _NAME_ final model
class FinalModelOptional_NAME_: FinalModel {

    var _buffer: Buffer
    var _offset: Int

    // Base field model value
    let value: _MODEL_

    required init() {
        let buffer = Buffer()
        let offset = 0

        _buffer = buffer
        _offset = offset

        value = _MODEL_(buffer: buffer, offset: offset)
    }

    required init(buffer: Buffer, offset: Int) {
        _buffer = buffer
        _offset = offset

        value = _MODEL_(buffer: buffer, offset: offset)
    }

    func fbeAllocationSize(value optional: _TYPE_) -> Int {
        return 1 + (optional != nil ? value.fbeAllocationSize(value: optional!) : 0)
    }

    func hasValue() -> Bool {
        if _buffer.offset + fbeOffset + 1 > _buffer.size {
            return false
        }

        let fbeHasValue = Int32(readInt8(offset: fbeOffset))
        return fbeHasValue != 0
    }

    public func verify() -> Int {
        if _buffer.offset + fbeOffset + 1 > _buffer.size {
            return Int.max
        }

        let fbeHasValue = Int(readInt8(offset: fbeOffset))
        if fbeHasValue == 0 {
            return 1
        }

        _buffer.shift(offset: fbeOffset + 1)
        let fbeResult = value.verify()
        _buffer.unshift(offset: fbeOffset + 1)
        return 1 + fbeResult
    }

    public func get(size: inout Size) -> _TYPE_ {
        if _buffer.offset + fbeOffset + 1 > _buffer.size {
            assertionFailure("Model is broken!")
            size.value = 0
            return nil
        }

        if !hasValue() {
            size.value = 1
            return nil
        }

        _buffer.shift(offset: fbeOffset + 1)
        let optional = value.get(size: &size)
        _buffer.unshift(offset: fbeOffset + 1)
        size.value += 1
        return optional
    }

    // Set the optional value
    public func set(value optional: _TYPE_) throws -> Int {
        if _buffer.offset + fbeOffset + 1 > _buffer.size {
            assertionFailure("Model is broken!")
            return 0
        }

        let fbeHasValue = optional != nil ? 1 : 0
        write(offset: fbeOffset, value: Int8(fbeHasValue))
        if fbeHasValue == 0 {
            return 1
        }

        _buffer.shift(offset: fbeOffset + 1)
        let size = try value.set(value: optional!)
        _buffer.unshift(offset: fbeOffset + 1)
        return 1 + size
    }
}
)CODE";

    std::string type_name = type;

    // Prepare code template
    code = std::regex_replace(code, std::regex("_DOMAIN_"), domain);
    code = std::regex_replace(code, std::regex("_NAME_"), name);
    code = std::regex_replace(code, std::regex("_TYPE_"), type_name);
    code = std::regex_replace(code, std::regex("_MODEL_"), model);
    code = std::regex_replace(code, std::regex("\n"), EndLine());

    Write(code);

    // Generate footer
    GenerateFooter();

    // Store the file
    WriteEnd();
    Store(file);
}

void GeneratorSwift::GenerateFBEFinalModelArray(const std::string& domain, const std::shared_ptr<Package>& p, const std::string& name, const std::string& type, const std::string& base, bool optional, const std::string& model)
{
    std::string package = ConvertPackage(p);
    CppCommon::Path path = (CppCommon::Path(_output) / CreatePackagePath(domain, package)) / "Fbe";

    // Create package path
    CppCommon::Directory::CreateTree(path);

    // Generate the file
    CppCommon::Path file = path / ("FinalModelArray" + name + ".swift");
    WriteBegin();

    // Generate headers
    GenerateHeader(CppCommon::Path(_input).filename().string());
    GenerateImports("", "Foundation");
    GenerateImports(domain, "Fbe");
    GenerateImports(p);

    std::string code = R"CODE(
// Fast Binary Encoding _NAME_ array final model
class FinalModelArray_NAME_: FinalModel {
    var _buffer: Buffer = Buffer()
    var _offset: Int = 0
    var _size: Int = 0

    private var _model: _MODEL_

    init(buffer: Buffer, offset: Int, size: Int) {
        _buffer = buffer
        _offset = offset
        _size = size

        _model = _MODEL_(buffer: buffer, offset: offset)
    }

    // Get the allocation size
    func fbeAllocationSize(value values: _ARRAY_) -> Int {
        var size: Int = 0
        for value in values {
            size += _model.fbeAllocationSize(value: value)
        }

        return size
    }

    // Check if the vector is valid
    public func verify() -> Int {
        if _buffer.offset + fbeOffset > _buffer.size {
            return Int.max
        }

        var size: Int = 0
        _model.fbeOffset = fbeOffset
        var i = _size
        while i > 0 {
            let offset = _model.verify()
            if offset == Int.max { return Int.max }
            _model.fbeShift(size: offset)
            size += offset
            i -= 1
        }
        return size
    }

    public func get(size: inout Size) -> _ARRAY_ {
        var values = _ARRAY_()

        if _buffer.offset + fbeOffset > _buffer.size {
            assertionFailure("Model is broken!")
            size.value = 0
            return values
        }

        size.value = 0
        var offset = Size()
        _model.fbeOffset = fbeOffset
        for _ in 0..._size {
            offset.value = 0
            let value = _model.get(size: &offset)
            values.append(value)
            _model.fbeShift(size: offset.value)
            size.value += offset.value
        }
        return values
    }

    public func get(values: inout _ARRAY_) -> Int {
        values.removeAll()

        if _buffer.offset + fbeOffset > _buffer.size {
            assertionFailure("Model is broken!")
            return 0
        }

        var size: Int = 0
        var offset = Size()
        _model.fbeOffset = fbeOffset
        for _ in 1..._size {
            offset.value = 0
            let value = _model.get(size: &offset)
            values.append(value)
            _model.fbeShift(size: offset.value)
            size += offset.value
        }
        return size
    }

    public func set(value values: _ARRAY_) throws -> Int {
        if _buffer.offset + fbeOffset > _buffer.size {
            assertionFailure("Model is broken!")
            return 0
        }

        var size: Int = 0
        _model.fbeOffset = fbeOffset
        for value in values {
            let offset = try _model.set(value: value)
            _model.fbeShift(size: offset)
            size += offset
        }
        return size
    }
}
)CODE";

    std::string type_name = type;

    // Prepare code template
    code = std::regex_replace(code, std::regex("_DOMAIN_"), domain);
    code = std::regex_replace(code, std::regex("_NAME_"), name);
    code = std::regex_replace(code, std::regex("_TYPE_"), type_name);
    code = std::regex_replace(code, std::regex("_MODEL_"), model);
    if (optional)
      code = std::regex_replace(code, std::regex("_ARRAY_"), "Array<" + type_name + ">");
    else
      code = std::regex_replace(code, std::regex("_ARRAY_"), "Array<" + type_name + ">");
    code = std::regex_replace(code, std::regex("\n"), EndLine());

    Write(code);

    // Generate footer
    GenerateFooter();

    // Store the file
    WriteEnd();
    Store(file);
}

void GeneratorSwift::GenerateFBEFinalModelVector(const std::string& domain, const std::shared_ptr<Package>& p, const std::string& name, const std::string& type, const std::string& model)
{
    std::string package = ConvertPackage(p);
    CppCommon::Path path = (CppCommon::Path(_output) / CreatePackagePath(domain, package)) / "Fbe";

    // Create package path
    CppCommon::Directory::CreateTree(path);

    // Generate the file
    CppCommon::Path file = path / ("FinalModelVector" + name + ".swift");
    WriteBegin();

    // Generate headers
    GenerateHeader(CppCommon::Path(_input).filename().string());
    GenerateImports("", "Foundation");
    GenerateImports(domain, "Fbe");
    GenerateImports(p);

    std::string code = R"CODE(
// Fast Binary Encoding _NAME_ vector final model
class FinalModelVector_NAME_: FinalModel {
    var _buffer: Buffer = Buffer()
    var _offset: Int = 0

    private var _model: _MODEL_

    init(buffer: Buffer, offset: Int) {
        _buffer = buffer
        _offset = offset

        _model = _MODEL_(buffer: buffer, offset: offset)
    }

    // Get the allocation size
    func fbeAllocationSize(value values: Array<_TYPE_>) -> Int {
        var size: Int = 4
        for i in 0..<values.count {
            size += _model.fbeAllocationSize(value: values[i])
        }

        return size
    }

    // Check if the vector is valid
    public func verify() -> Int {
        if _buffer.offset + fbeOffset + 4 > _buffer.size {
            return Int.max
        }

        let fbeVectorSize = Int(readUInt32(offset: fbeOffset))

        var size: Int = 4
        _model.fbeOffset = fbeOffset + 4
        var i = fbeVectorSize
        while i > 0 {
            let offset = _model.verify()
            if offset == Int.max { return Int.max }
            _model.fbeShift(size: offset)
            size += offset
            i -= 1
        }
        return size
    }

    public func get(values: inout Array<_TYPE_>) -> Int {
        values.removeAll()

        if _buffer.offset + fbeOffset + 4 > _buffer.size {
            assertionFailure("Model is broken!")
            return 0
        }

        let fbeVectorSize = Int(readUInt32(offset: fbeOffset))
        if fbeVectorSize == 0 {
            return 4
        }

        values.reserveCapacity(fbeVectorSize)

        var size: Int = 4
        var offset = Size()
        _model.fbeOffset = fbeOffset + 4
        for _ in 1...fbeVectorSize {
            offset.value = 0
            let value = _model.get(size: &offset)
            values.append(value)
            _model.fbeShift(size: offset.value)
            size += offset.value
        }
        return size
    }

    public func set(value values: Array<_TYPE_>) throws -> Int {
        if _buffer.offset + fbeOffset + 4 > _buffer.size {
            assertionFailure("Model is broken!")
            return 0
        }

        write(offset: fbeOffset, value: UInt32(values.count))

        var size: Int = 4
        _model.fbeOffset = fbeOffset + 4
        for i in 0..<values.count {
            let offset = try _model.set(value: values[i])
            _model.fbeShift(size: offset)
            size += offset
        }
        return size
    }
}
)CODE";

    std::string type_name = IsPackageType(type) ? type : (domain + package + "." + type);

    // Prepare code template
    code = std::regex_replace(code, std::regex("_DOMAIN_"), domain);
    code = std::regex_replace(code, std::regex("_NAME_"), name);
    code = std::regex_replace(code, std::regex("_TYPE_"), type_name);
    code = std::regex_replace(code, std::regex("_MODEL_"), model);
    code = std::regex_replace(code, std::regex("\n"), EndLine());

    Write(code);

    // Generate footer
    GenerateFooter();

    // Store the file
    WriteEnd();
    Store(file);
}

void GeneratorSwift::GenerateFBEFinalModelMap(const std::string& domain, const std::shared_ptr<Package>& p, const std::string& key_name, const std::string& key_type, const std::string& key_model, const std::string& value_name, const std::string& value_type, const std::string& value_model)
{
    std::string package = ConvertPackage(p);
    CppCommon::Path path = (CppCommon::Path(_output) / CreatePackagePath(domain, package)) / "Fbe";

    // Create package path
    CppCommon::Directory::CreateTree(path);

    // Generate the file
    CppCommon::Path file = path / ("FinalModelMap" + key_name + value_name + ".swift");
    WriteBegin();

    // Generate headers
    GenerateHeader(CppCommon::Path(_input).filename().string());
    GenerateImports("", "Foundation");
    GenerateImports(domain, "Fbe");
    GenerateImports(p);

    std::string code = R"CODE(
// Fast Binary Encoding _KEY_NAME_->_VALUE_NAME_ map final model
class FinalModelMap_KEY_NAME__VALUE_NAME_: FinalModel {
    var _buffer: Buffer = Buffer()
    var _offset: Int = 0

    private var _modelKey: _KEY_MODEL_
    private var _modelValue: _VALUE_MODEL_

    init(buffer: Buffer, offset: Int) {
        _buffer = buffer
        _offset = offset

        _modelKey = _KEY_MODEL_(buffer: buffer, offset: offset)
        _modelValue = _VALUE_MODEL_(buffer: buffer, offset: offset)
    }

    // Get the allocation size
    func fbeAllocationSize(value values: Dictionary<_KEY_TYPE_, _VALUE_TYPE_>) -> Int {
        var size: Int = 4
        for (key, value) in values {
            size += _modelKey.fbeAllocationSize(value: key)
            size += _modelValue.fbeAllocationSize(value: value)
        }

        return size
    }

    // Check if the vector is valid
    public func verify() -> Int {
        if _buffer.offset + fbeOffset + 4 > _buffer.size {
            return Int.max
        }

        let fbeMapSize = Int(readUInt32(offset: fbeOffset))

        var size: Int = 4
        _modelKey.fbeOffset = fbeOffset + 4
        _modelValue.fbeOffset = fbeOffset + 4
        var i = fbeMapSize
        while i > 0 {
            let offsetKey = _modelKey.verify()
            if offsetKey == Int.max { return Int.max }
            _modelKey.fbeShift(size: offsetKey)
            _modelValue.fbeShift(size: offsetKey)
            size += offsetKey
            let offsetValue = _modelValue.verify()
            if offsetValue == Int.max { return Int.max }
            _modelKey.fbeShift(size: offsetValue)
            _modelValue.fbeShift(size: offsetValue)
            size += offsetValue
            i -= 1
        }
        return size
    }

    public func get(values: inout Dictionary<_KEY_TYPE_, _VALUE_TYPE_>) -> Int {
        values.removeAll()

        if _buffer.offset + fbeOffset + 4 > _buffer.size {
            assertionFailure("Model is broken!")
            return 0
        }

        let fbeMapSize = Int(readUInt32(offset: fbeOffset))
        if fbeMapSize == 0 {
            return 4
        }

        var size: Int = 4
        var offset = Size()
        _modelKey.fbeOffset = fbeOffset + 4
        _modelValue.fbeOffset = fbeOffset + 4
        for _ in 1...fbeMapSize {
            offset.value = 0
            let key = _modelKey.get(size: &offset)
            _modelKey.fbeShift(size: offset.value)
            _modelValue.fbeShift(size: offset.value)
            size += offset.value
            offset.value = 0
            let value = _modelValue.get(size: &offset)
            _modelKey.fbeShift(size: offset.value)
            _modelValue.fbeShift(size: offset.value)
            size += offset.value
            values[key] = value
        }
        return size
    }

    public func set(value values: Dictionary<_KEY_TYPE_, _VALUE_TYPE_>) throws -> Int {
        if _buffer.offset + fbeOffset + 4 > _buffer.size {
            assertionFailure("Model is broken!")
            return 0
        }

        write(offset: fbeOffset, value: UInt32(values.count))

        var size: Int = 4
        _modelKey.fbeOffset = fbeOffset + 4
        _modelValue.fbeOffset = fbeOffset + 4
        for (key, value) in values {
            let offsetKey = try _modelKey.set(value: key)
            _modelKey.fbeShift(size: offsetKey)
            _modelValue.fbeShift(size: offsetKey)
            let offsetValue = try _modelValue.set(value: value)
            _modelKey.fbeShift(size: offsetValue)
            _modelValue.fbeShift(size: offsetValue)
            size += offsetKey + offsetValue
        }
        return size
    }
}
)CODE";

  std::string key_type_name = IsPackageType(key_type) ? key_type : (domain + package + "." + key_type);
  std::string value_type_name = IsPackageType(value_type) ? value_type : (domain + package + "." + value_type);

    // Prepare code template
    code = std::regex_replace(code, std::regex("_DOMAIN_"), domain);
    code = std::regex_replace(code, std::regex("_KEY_NAME_"), key_name);
    code = std::regex_replace(code, std::regex("_KEY_TYPE_"), key_type_name);
    code = std::regex_replace(code, std::regex("_KEY_MODEL_"), key_model);
    code = std::regex_replace(code, std::regex("_VALUE_NAME_"), value_name);
    code = std::regex_replace(code, std::regex("_VALUE_TYPE_"), value_type_name);
    code = std::regex_replace(code, std::regex("_VALUE_MODEL_"), value_model);
    code = std::regex_replace(code, std::regex("\n"), EndLine());

    Write(code);

    // Generate footer
    GenerateFooter();

    // Store the file
    WriteEnd();
    Store(file);
}

void GeneratorSwift::GenerateFBEFinalModelEnumFlags(const std::string& domain, const std::string& package, const std::string& name, const std::string& type)
{
    CppCommon::Path path = (CppCommon::Path(_output) / CreatePackagePath(domain, package)) / "Fbe";

    // Create package path
    CppCommon::Directory::CreateTree(path);

    // Generate the file
    CppCommon::Path file = path / ("FinalModel" + name + ".swift");
    WriteBegin();

    // Generate headers
    GenerateHeader(CppCommon::Path(_input).filename().string());
    GenerateImports(domain, "Fbe");

    std::string code = R"CODE(
// Fast Binary Encoding _NAME_ final model
public class FinalModel_NAME_: FinalModel {

    public var _buffer: Buffer
    public var _offset: Int

    // Final size
    public let fbeSize: Int = _SIZE_

    public init(buffer: Buffer = Buffer(), offset: Int = 0) {
        _buffer = buffer
        _offset = offset
    }

    // Get the allocation size
    public func fbeAllocationSize(value: _NAME_) -> Int { fbeSize }

    public func verify() -> Int {
        if _buffer.offset + fbeOffset + fbeSize > _buffer.size {
            return Int.max
        }

        return fbeSize
    }

    // Get the value
    public func get(size: inout Size) -> _NAME_ {
        if _buffer.offset + fbeOffset + fbeSize > _buffer.size {
            return _NAME_()
        }

        size.value = fbeSize
        return _NAME_(value: _READ_(offset: fbeOffset))
    }

    // Set the value
    public func set(value: _NAME_) throws -> Int {
        if _buffer.offset + fbeOffset + fbeSize > _buffer.size {
            assertionFailure("Model is broken!")
            return 0
        }

        write(offset: fbeOffset, value: value.raw)
        return fbeSize
    }
}
)CODE";

    // Prepare code template
    code = std::regex_replace(code, std::regex("_DOMAIN_"), domain);
    code = std::regex_replace(code, std::regex("_PACKAGE_"), package);
    code = std::regex_replace(code, std::regex("_NAME_"), name);
    code = std::regex_replace(code, std::regex("_SIZE_"), ConvertEnumSize(type));
    code = std::regex_replace(code, std::regex("_READ_"), ConvertEnumRead(type));
    code = std::regex_replace(code, std::regex("\n"), EndLine());

    Write(code);

    // Generate footer
    GenerateFooter();

    // Store the file
    WriteEnd();
    Store(file);
}

void GeneratorSwift::GenerateFBESender(const std::string& domain, const std::string& package)
{
    CppCommon::Path path = CppCommon::Path(_output) / CreatePackagePath(domain, package);

    // Generate the file
    CppCommon::Path file = path / "SenderProtocol.swift";
    WriteBegin();

    // Generate headers
    GenerateHeader("FBE");
    GenerateImports("", "Foundation");

    std::string code = R"CODE(
// Fast Binary Encoding base sender
public protocol SenderProtocol: class {

    // Get the bytes buffer
    var buffer: Buffer { get set }

    // Get the final protocol flag
    var final: Bool { get set }
}

public extension SenderProtocol {

    func build(with final: Bool) {
        self.final = final
    }

    func build(with buffer: Buffer, final: Bool) {
        self.buffer = buffer
        self.final = final
    }

    // Reset the sender buffer
    func reset() { buffer.reset() }

    // Send serialized buffer.
    // Direct call of the method requires knowledge about internals of FBE models serialization.
    // Use it with care!
    func sendSerialized(listener: SenderListener, serialized: Int) throws -> Int {
        if serialized <= 0 {
            assertionFailure("Invalid size of the serialized buffer!")
            return 0
        }

        // Shift the send buffer
        buffer.shift(offset: serialized)

        // Send the value
        let sent = try listener.onSend(buffer: buffer.data, offset: 0, size: buffer.size)
        try _ = buffer.remove(offset: 0, size: sent)
        return sent
    }
}
)CODE";

    // Prepare code template
    code = std::regex_replace(code, std::regex("\n"), EndLine());

    Write(code);

    // Generate footer
    GenerateFooter();

    // Store the file
    WriteEnd();
    Store(file);
}

void GeneratorSwift::GenerateFBEReceiver(const std::string& domain, const std::string& package)
{
    CppCommon::Path path = CppCommon::Path(_output) / CreatePackagePath(domain, package);

    // Generate the file
    CppCommon::Path file = path / "ReceiverProtocol.swift";
    WriteBegin();

    // Generate headers
    GenerateHeader("FBE");
    GenerateImports("", "Foundation");

    std::string code = R"CODE(
// Fast Binary Encoding base receiver
public protocol ReceiverProtocol: class {

    // Get the bytes buffer
    var buffer: Buffer { get set }

    // Get the final protocol flag
    var final: Bool { get set }

    // Receive message handler
    func onReceive(type: Int, buffer: Data, offset: Int, size: Int) -> Bool
}

public extension ReceiverProtocol {

    func build(final: Bool) {
        self.final = final
    }

    func build(with buffer: Buffer, final: Bool) {
        self.buffer = buffer
        self.final = final
    }

    // Reset the receiver buffer
    func reset() { buffer.reset() }

    // Receive data
    func receive(buffer: Buffer) throws { try receive(buffer: buffer, offset: 0, size: buffer.size) }

    func receive(buffer: Buffer, offset: Int, size: Int) throws {
       // print("receive start: \(self.buffer.data.count)")
        defer {
           // print("receive end: \(self.buffer.data.count)")
        }
        assert((offset + size) <= buffer.data.count, "Invalid offset & size!")
        if (offset + size) > buffer.data.count {
            throw NSError()
        }

        if size == 0 {
            return
        }

        // Storage buffer
        var offset0 = self.buffer.offset
        var offset1 = self.buffer.size
        var size1 = self.buffer.size

        // Receive buffer
        var offset2: Int = 0

        // While receive buffer is available to handle...
        while offset2 < size {
            var messageBuffer: Buffer? = nil
            var messageOffset: Int = 0
            var messageSize: Int = 0

            // Try to receive message size
            var messageSizeCopied = false
            var messageSizeFound = false
            while !messageSizeFound {
                // Look into the storage buffer
                if offset0 < size1 {
                    var count = min(size1 - offset0, 4)
                    if count == 4 {
                        messageSizeCopied = true
                        messageSizeFound = true
                        messageSize = Int(Buffer.readUInt32(buffer: self.buffer, offset: offset0))
                        offset0 += 4
                        break
                    } else {
                        // Fill remaining data from the receive buffer
                        if offset2 < size {
                            count = min(size - offset2, 4 - count)

                            // Allocate and refresh the storage buffer
                            try _ = self.buffer.allocate(size: count)
                            size1 += count

                            self.buffer.data[offset1..<offset1 + count] = buffer.data[(offset + offset2)..<(offset + offset2) + count]
                            offset1 += count
                            offset2 += count
                            continue
                        } else {
                            break
                        }
                    }
                }

                // Look into the receive buffer
                if offset2 < size {
                    let count = min(size - offset2, 4)
                    if count == 4 {
                        messageSizeFound = true
                        messageSize = Int(Buffer.readUInt32(buffer: buffer, offset: offset + offset2))
                        offset2 += 4
                        break
                    } else {
                        // Allocate and refresh the storage buffer
                        try _ = self.buffer.allocate(size: count)
                        size1 += count

                        self.buffer.data[offset1..<offset1 + count] = buffer.data[(offset + offset2)..<(offset + offset2) + count]
                        offset1 += count
                        offset2 += count
                        continue
                    }
                } else {
                    break
                }
            }

            if !messageSizeFound {
                return
            }

            // Check the message full size
            let minSize = {
                return final ? 4 + 4 : 4 + 4 + 4 + 4
            }()
            assert(messageSize >= minSize, "Invalid receive data!")
            if messageSize < minSize {
                return
            }

            // Try to receive message body
            var messageFound = false
            while !messageFound {
                // Look into the storage buffer
                if offset0 < size1 {
                    var count = min(size1 - offset0, messageSize - 4)
                    if count == (messageSize - 4) {
                        messageFound = true
                        messageBuffer = self.buffer
                        messageOffset = offset0 - 4
                        offset0 += messageSize - 4
                        break
                    } else {
                        // Fill remaining data from the receive buffer
                        if offset2 < size {
                            // Copy message size into the storage buffer
                            if !messageSizeCopied {
                                // Allocate and refresh the storage buffer
                                try _ = self.buffer.allocate(size: 4)
                                size1 += 4

                                Buffer.write(buffer: &self.buffer, offset: offset0, value: UInt32(messageSize))
                                offset0 += 4
                                offset1 += 4

                                messageSizeCopied = true
                            }

                            count = min(size - offset2, messageSize - 4 - count)

                            // Allocate and refresh the storage buffer
                            try _ = self.buffer.allocate(size: count)
                            size1 += count

                            self.buffer.data[offset1..<offset1 + count] = buffer.data[(offset + offset2)..<(offset + offset2) + count]
                            offset1 += count
                            offset2 += count
                            continue
                        } else {
                            break
                        }

                    }
                }

                // Look into the receive buffer
                if offset2 < size {
                    let count = min(size - offset2, messageSize - 4)
                    if !messageSizeCopied && (count == (messageSize - 4)) {
                        messageFound = true
                        messageBuffer = buffer
                        messageOffset = offset + offset2 - 4
                        offset2 += messageSize - 4
                        break
                    } else {
                        // Copy message size into the storage buffer
                        if !messageSizeCopied {
                            // Allocate and refresh the storage buffer
                            try _ = self.buffer.allocate(size: 4)
                            size1 += 4

                            Buffer.write(buffer: &self.buffer, offset: offset0, value: UInt32(messageSize))
                            offset0 += 4
                            offset1 += 4

                            messageSizeCopied = true
                        }

                        // Allocate and refresh the storage buffer
                        try _ = self.buffer.allocate(size: count)
                        size1 += count
                        self.buffer.data[offset1..<offset1 + count] = buffer.data[(offset + offset2)..<(offset + offset2) + count]
                        offset1 += count
                        offset2 += count
                        continue
                    }
                } else {
                    break
                }
            }

            if !messageFound {
                // Copy message size into the storage buffer
                if !messageSizeCopied {
                    // Allocate and refresh the storage buffer
                    try _ = self.buffer.allocate(size: 4)
                    size1 += 4

                    Buffer.write(buffer: &self.buffer, offset: offset0, value: UInt32(messageSize))
                    offset0 += 4
                    offset1 += 4

                    //@Suppress("UNUSED_VALUE")
                    messageSizeCopied = true
                }
                return
            }

            if let messageBuffer = messageBuffer {
                let fbeStructSize: Int
                let fbeStructType: Int

                // Read the message parameters
                if final {
                    fbeStructSize = Int(Buffer.readUInt32(buffer: messageBuffer, offset: messageOffset))
                    fbeStructType = Int(Buffer.readUInt32(buffer: messageBuffer, offset: messageOffset + 4))
                } else {
                    let fbeStructOffset = Int(Buffer.readUInt32(buffer: messageBuffer, offset: messageOffset + 4))
                    fbeStructSize = Int(Buffer.readUInt32(buffer: messageBuffer, offset: messageOffset + fbeStructOffset))
                    fbeStructType = Int(Buffer.readUInt32(buffer: messageBuffer, offset: messageOffset + fbeStructOffset + 4))
                }

                // Handle the message
                _ = onReceive(type: fbeStructType, buffer: messageBuffer.data, offset: messageOffset, size: messageSize)
            }

            // Reset the storage buffer
            self.buffer.reset()

            // Refresh the storage buffer
            offset0 = self.buffer.offset
            offset1 = self.buffer.size
            size1 = self.buffer.size
        }
    }
}
)CODE";

    // Prepare code template
    code = std::regex_replace(code, std::regex("\n"), EndLine());

    Write(code);

    // Generate footer
    GenerateFooter();

    // Store the file
    WriteEnd();
    Store(file);
}

void GeneratorSwift::GenerateFBEReceiverListener(const std::string& domain, const std::string& package)
{
    CppCommon::Path path = CppCommon::Path(_output) / CreatePackagePath(domain, package);

    // Generate the file
    CppCommon::Path file = path / "Listeners.swift";
    WriteBegin();

    // Generate headers
    GenerateHeader("FBE");
    GenerateImports("", "Foundation");

    std::string code = R"CODE(
public protocol LogListener {
    // Enable/Disable logging
    var logging: Bool { get set }
}

// Fast Binary Encoding log listener
public protocol ReceiveLogListener: LogListener {

    // Receive log message handler
    func onReceiveLog(message: String)
}

public extension ReceiveLogListener {
    func onReceiveLog(message: String) {}
}

public protocol SenderListener: LogListener {
    // Send message handler
    func onSend(buffer: Data, offset: Int, size: Int) throws -> Int

    // Send log message handler
    func onSendLog(message: String)
}

public extension SenderListener {
    func onSend(buffer: Data, offset: Int, size: Int) throws -> Int { return 0 }
    func onSendLog(message: String) {}
}
)CODE";

    // Prepare code template
    code = std::regex_replace(code, std::regex("\n"), EndLine());

    Write(code);

    // Generate footer
    GenerateFooter();

    // Store the file
    WriteEnd();
    Store(file);
}

void GeneratorSwift::GenerateFBEClient(const std::string& domain, const std::string& package)
{
    CppCommon::Path path = CppCommon::Path(_output) / CreatePackagePath(domain, package);

    // Generate the file
    CppCommon::Path file = path / "ClientProtocol.swift";
    WriteBegin();

    // Generate headers
    GenerateHeader("FBE");
    GenerateImports("", "Foundation");

    std::string code = R"CODE(
// Fast Binary Encoding base client
public protocol ClientProtocol: class {

    // Get the send bytes buffer
    var sendBuffer: Buffer { get set }

    // Get the receive bytes buffer
    var receiveBuffer: Buffer { get set }

    // Get the final protocol flag
    var final: Bool { get set }

    // Receive message handler
    func onReceive(type: Int, buffer: Data, offset: Int, size: Int) -> Bool
}

public extension ClientProtocol {

     func build(with final: Bool) {
        self.final = final
    }

    func build(with sendBuffer: Buffer, receiveBuffer: Buffer, final: Bool) {
        self.sendBuffer = sendBuffer
        self.receiveBuffer = receiveBuffer
        self.final = final
    }

    func reset() {
        sendBuffer.reset()
        receiveBuffer.reset()
    }

    // Send serialized buffer.
    // Direct call of the method requires knowledge about internals of FBE models serialization.
    // Use it with care!
    func sendSerialized(listener: SenderListener, serialized: Int) throws -> Int {
        assert(serialized > 0, "Invalid size of the serialized buffer!")

        if serialized <= 0 {
            return 0
        }

        // Shift the send buffer
        sendBuffer.shift(offset: serialized)

        // Send the value
        let sent = try listener.onSend(buffer: sendBuffer.data, offset: 0, size: sendBuffer.size)
        try sendBuffer.remove(offset: 0, size: sent)
        return sent
    }

    // Receive data
    func receive(buffer: Data) throws {
        let buffer = Buffer(buffer: buffer)
        try receive(buffer: buffer, offset: 0, size: buffer.data.count)
    }

    func receive(buffer: Buffer, offset: Int, size: Int) throws {
        assert((offset + size) <= buffer.data.count, "Invalid offset & size!")

        if (offset + size) > buffer.data.count {
            throw NSException(name: .invalidArgumentException, reason: "Invalid allocation size!") as! Error
        }

        if size == 0 {
            return
        }

        // Storage buffer
        var offset0 = self.receiveBuffer.offset
        var offset1 = self.receiveBuffer.size
        var size1 = self.receiveBuffer.size

        // Receive buffer
        var offset2: Int = 0

        // While receive buffer is available to handle...
        while offset2 < size {
            var messageBuffer: Buffer?
            var messageOffset: Int = 0
            var messageSize: Int = 0

            // Try to receive message size
            var messageSizeCopied = false
            var messageSizeFound = false
            while !messageSizeFound {
                // Look into the storage buffer
                if offset0 < size1 {
                    var count = min(size1 - offset0, 4)
                    if count == 4 {
                        messageSizeCopied = true
                        messageSizeFound = true
                        messageSize = Int(Buffer.readUInt32(buffer: self.receiveBuffer, offset: offset0))
                        offset0 += 4
                        break
                    } else {
                        // Fill remaining data from the receive buffer
                        if offset2 < size {
                            count = min(size - offset2, 4 - count)

                            // Allocate and refresh the storage buffer
                            try _ = self.receiveBuffer.allocate(size: count)
                            size1 += count

                            self.receiveBuffer.data[offset1..<offset1 + count] = buffer.data[(offset + offset2)..<(offset + offset2) + count]
                            offset1 += count
                            offset2 += count
                            continue
                        } else {
                            break
                        }
                    }
                }

                // Look into the receive buffer
                if offset2 < size {
                    let count = min(size - offset2, 4)
                    if count == 4 {
                        messageSizeFound = true
                        messageSize = Int(Buffer.readUInt32(buffer: buffer, offset: offset + offset2))
                        offset2 += 4
                        break
                    } else {
                        // Allocate and refresh the storage buffer
                        try _ = self.receiveBuffer.allocate(size: count)
                        size1 += count

                        self.receiveBuffer.data[offset1..<offset1 + count] = buffer.data[(offset + offset2)..<(offset + offset2) + count]
                        offset1 += count
                        offset2 += count
                        continue
                    }
                } else {
                    break
                }

            }

            if !messageSizeFound {
                return
            }

            // Check the message full size
            let minSize: Int = {
                return final ? 4 + 4 : 4 + 4 + 4 + 4
            }()

            assert(messageSize >= minSize, "Invalid receive data!")
            if messageSize < minSize {
                return
            }

            // Try to receive message body
            var messageFound = false
            while !messageFound {
                // Look into the storage buffer
                if offset0 < size1 {
                    var count = min(size1 - offset0, messageSize - 4)
                    if count == (messageSize - 4) {
                        messageFound = true
                        messageBuffer = self.receiveBuffer
                        messageOffset = offset0 - 4
                        offset0 += messageSize - 4
                        break
                    } else {
                        // Fill remaining data from the receive buffer
                        if offset2 < size {
                            // Copy message size into the storage buffer
                            if !messageSizeCopied {
                                // Allocate and refresh the storage buffer
                                try _ = self.receiveBuffer.allocate(size: 4)
                                size1 += 4
                                Buffer.write(buffer: &self.receiveBuffer, offset: offset0, value: UInt32(messageSize))
                                offset0 += 4
                                offset1 += 4

                                messageSizeCopied = true
                            }

                            count = min(size - offset2, messageSize - 4 - count)

                            // Allocate and refresh the storage buffer
                            try _ = self.receiveBuffer.allocate(size: count)
                            size1 += count

                            self.receiveBuffer.data[offset1..<offset1 + count] = buffer.data[(offset + offset2)..<(offset + offset2) + count]
                            offset1 += count
                            offset2 += count
                            continue
                        } else {
                            break
                        }
                    }
                }

                // Look into the receive buffer
                if offset2 < size {
                    let count = min(size - offset2, messageSize - 4)
                    if !messageSizeCopied && (count == (messageSize - 4)) {
                        messageFound = true
                        messageBuffer = buffer
                        messageOffset = offset + offset2 - 4
                        offset2 += messageSize - 4
                        break
                    } else {
                        // Copy message size into the storage buffer
                        if !messageSizeCopied {
                            // Allocate and refresh the storage buffer
                            try _ = self.receiveBuffer.allocate(size: 4)
                            size1 += 4

                            Buffer.write(buffer: &self.receiveBuffer, offset: offset0, value: UInt32(messageSize))
                            offset0 += 4
                            offset1 += 4

                            messageSizeCopied = true
                        }

                        // Allocate and refresh the storage buffer
                        try _ = self.receiveBuffer.allocate(size: count)
                        size1 += count

                        self.receiveBuffer.data[offset1..<offset1 + count] = buffer.data[(offset + offset2)..<(offset + offset2) + count]
                        offset1 += count
                        offset2 += count
                        continue
                    }
                } else {
                    break
                }
            }

            if !messageFound {
                // Copy message size into the storage buffer
                if !messageSizeCopied {
                    // Allocate and refresh the storage buffer
                    try _ = self.receiveBuffer.allocate(size: 4)
                    size1 += 4

                    Buffer.write(buffer: &self.receiveBuffer, offset: offset0, value: UInt32(messageSize))
                    offset0 += 4
                    offset1 += 4

                    messageSizeCopied = true
                }
                return
            }

            if let messageBuffer = messageBuffer {
                let fbeStructSize: Int
                let fbeStructType: Int

                // Read the message parameters
                if final {
                    fbeStructSize = Int(Buffer.readUInt32(buffer: messageBuffer, offset: messageOffset))
                    fbeStructType = Int(Buffer.readUInt32(buffer: messageBuffer, offset: messageOffset + 4))
                } else {
                    let fbeStructOffset = Int(Buffer.readUInt32(buffer: messageBuffer, offset: messageOffset + 4))
                    fbeStructSize = Int(Buffer.readUInt32(buffer: messageBuffer, offset: messageOffset + fbeStructOffset))
                    fbeStructType = Int(Buffer.readUInt32(buffer: messageBuffer, offset: messageOffset + fbeStructOffset + 4))
                }

                // Handle the message
                _ = onReceive(type: fbeStructType, buffer: messageBuffer.data, offset: messageOffset, size: messageSize)
            }

            // Reset the storage buffer
            self.receiveBuffer.reset()

            // Refresh the storage buffer
            offset0 = self.receiveBuffer.offset
            offset1 = self.receiveBuffer.size
            size1 = self.receiveBuffer.size
        }
    }
}
)CODE";

    // Prepare code template
    code = std::regex_replace(code, std::regex("\n"), EndLine());

    Write(code);

    // Generate footer
    GenerateFooter();

    // Store the file
    WriteEnd();
    Store(file);
}

void GeneratorSwift::GenerateContainers(const std::shared_ptr<Package>& p, bool final)
{
    std::string domain = ConvertDomain(p);
    std::string package = ConvertPackage(p);
    CppCommon::Path path = CppCommon::Path(_output) / CreatePackagePath(domain, package);

    // Create package path
    CppCommon::Directory::CreateTree(path);

    if (p->body)
    {
        // Check all structs in the package
        for (const auto& s : p->body->structs)
        {
            if (s->body)
            {
                // Check all fields in the struct
                for (const auto& field : s->body->fields)
                {
                    if (field->array)
                    {
                        if (final)
                            GenerateFBEFinalModelArray(domain, p, (field->optional ? "Optional" : "") + ConvertTypeFieldName(*field->type), ConvertTypeFieldType(domain, *field->type, field->optional), *field->type, field->optional, ConvertTypeFieldDeclaration(domain, *field->type, field->optional, final));
                        else
                            GenerateFBEFieldModelArray(domain, p, (field->optional ? "Optional" : "") + ConvertTypeFieldName(*field->type), ConvertTypeFieldType(domain, *field->type, field->optional), *field->type, field->optional, ConvertTypeFieldDeclaration(domain, *field->type, field->optional, final));
                    }
                    if (field->vector || field->list || field->set)
                    {
                        if (final)
                            GenerateFBEFinalModelVector(domain, p, (field->optional ? "Optional" : "") + ConvertTypeFieldName(*field->type), ConvertTypeFieldType(domain, *field->type, field->optional), ConvertTypeFieldDeclaration(domain, *field->type, field->optional, final));
                        else {
                          GenerateFBEFieldModelVector(domain, p, (field->optional ? "Optional" : "") + ConvertTypeFieldName(*field->type), ConvertTypeFieldType(domain, *field->type, field->optional), ConvertTypeFieldDeclaration(domain, *field->type, field->optional, final));
                        }
                    }
                    if (field->map || field->hash)
                    {
                        if (final)
                            GenerateFBEFinalModelMap(domain, p, ConvertTypeFieldName(*field->key), ConvertTypeFieldType(domain, *field->key, false), ConvertTypeFieldDeclaration(domain, *field->key, false, final), (field->optional ? "Optional" : "") + ConvertTypeFieldName(*field->type), ConvertTypeFieldType(domain, *field->type, field->optional), ConvertTypeFieldDeclaration(domain, *field->type, field->optional, final));
                        else
                            GenerateFBEFieldModelMap(domain, p, ConvertTypeFieldName(*field->key), ConvertTypeFieldType(domain, *field->key, false), ConvertTypeFieldDeclaration(domain, *field->key, false, final), (field->optional ? "Optional" : "") + ConvertTypeFieldName(*field->type), ConvertTypeFieldType(domain, *field->type, field->optional), ConvertTypeFieldDeclaration(domain, *field->type, field->optional, final));
                    }
                    if (field->optional)
                    {
                        if (final)
                            GenerateFBEFinalModelOptional(domain, p, ConvertTypeFieldName(*field->type), ConvertTypeFieldType(domain, *field->type, field->optional), ConvertTypeFieldDeclaration(domain, *field->type, false, final));
                        else
                            GenerateFBEFieldModelOptional(domain, p, ConvertTypeFieldName(*field->type), ConvertTypeFieldType(domain, *field->type, field->optional), ConvertTypeFieldDeclaration(domain, *field->type, false, final));
                    }
                }
            }
        }
    }
}

void GeneratorSwift::GeneratePackage(const std::shared_ptr<Package>& p)
{
    std::string domain = ConvertDomain(p);
    std::string package = ConvertPackage(p);
    CppCommon::Path path = CppCommon::Path(_output) / CreatePackagePath(domain, package);

    // Create package path
    CppCommon::Directory::CreateTree(path);

    CppCommon::Path packagePath = CppCommon::Path(_output) / package;

    // Create FBE package path
    CppCommon::Directory::CreateTree(packagePath);

    // Generate the file
    CppCommon::Path file = packagePath / "Package.swift";
    WriteBegin();

    std::string code = R"CODE(// swift-tools-version:5.1
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "_NAME_",
    products: [
        // Products define the executables and libraries produced by a package, and make them visible to other packages.
        .library(
            name: "_NAME_",
            targets: ["_NAME_"])
    ],
    dependencies: [
        // Dependencies declare other packages that this package depends on.
        _DEPENDENCIES_RES_
    ],
    targets: [
        // Targets are the basic building blocks of a package. A target can define a module or a test suite.
        // Targets can depend on other targets in this package, and on products in packages which this package depends on.
        .target(
            name: "_NAME_",
            dependencies: [_DEPENDENCIES_]),
    ]
)
)CODE";

    std::string dependenciesRes = std::string(".package(path: \"../") + "Fbe" + std::string("\")");
    if (p->import)
        for (const auto& import : p->import->imports)
            dependenciesRes = dependenciesRes + std::string(",\n        .package(path: \"../") + ConvertPackageName(*import) + std::string("\")");

    std::string dependencies = std::string("\"") + domain + "Fbe" + std::string("\"");
    if (p->import)
        for (const auto& import : p->import->imports)
            dependencies = dependencies + std::string(", \"") + domain + ConvertPackageName(*import) + std::string("\"");

    // Prepare code template
    code = std::regex_replace(code, std::regex("_NAME_"), domain + package);
    code = std::regex_replace(code, std::regex("_DEPENDENCIES_RES_"), dependenciesRes);
    code = std::regex_replace(code, std::regex("_DEPENDENCIES_"), dependencies);
    code = std::regex_replace(code, std::regex("\n"), EndLine());
    Write(code);

    // Generate footer
    GenerateFooter();

    // Store the file
    WriteEnd();
    Store(file);

    // Generate namespace
    if (p->body)
    {
        // Generate child enums
        for (const auto& child_e : p->body->enums)
            GenerateEnum(p, child_e, path);

        // Generate child flags
        for (const auto& child_f : p->body->flags)
            GenerateFlags(p, child_f, path);

        // Generate child structs
        for (const auto& child_s : p->body->structs)
            GenerateStruct(p, child_s, path);
    }

    // Generate containers
    GenerateContainers(p, false);
    if (Final())
        GenerateContainers(p, true);

    // Generate protocol
    if (Proto())
    {
        // Generate protocol version
        GenerateProtocolVersion(p);

        // Generate sender & receiver
        GenerateSender(p, false);
        GenerateReceiver(p, false);
        GenerateReceiverListener(p, false);
        GenerateProxy(p, false);
        GenerateProxyListener(p, false);
        GenerateClient(p, false);
        if (Final())
        {
            GenerateSender(p, true);
            GenerateReceiver(p, true);
            GenerateReceiverListener(p, true);
            GenerateClient(p, true);
        }
    }
}

void GeneratorSwift::GenerateEnum(const std::shared_ptr<Package>& p, const std::shared_ptr<EnumType>& e, const CppCommon::Path& path)
{
    std::string enum_name = *e->name + "Enum";

    // Generate the output file
    CppCommon::Path output = path / (enum_name + ".swift");
    WriteBegin();

    // Generate enum header
    GenerateHeader(CppCommon::Path(_input).filename().string());
    GenerateImports("", "Foundation");

    std::string enum_type = (e->base && !e->base->empty()) ? *e->base : "int32";
    std::string enum_base_type = ConvertEnumType(enum_type);
    std::string enum_mapping_type = ConvertEnumBase(enum_type);
    std::string enum_to = ConvertEnumTo(enum_type);

    // Generate enum body
    WriteLine();
    WriteLineIndent("public enum " + enum_name + " {");
    Indent(1);
    WriteLineIndent("typealias RawValue = " + enum_mapping_type);
    if (e->body)
    {
        for (const auto& value : e->body->values)
        {
            WriteIndent("case " + *value->name);
            WriteLine();
        }
        WriteLine();
    }

    if (e->body)
    {
        int index = 0;
        std::string last = ConvertEnumConstant(enum_type, enum_type, (enum_type == "char"  ? "\"0\"" : "0"), false);
        WriteLineIndent("var rawValue: RawValue {");
        Indent(1);
        WriteLineIndent("switch self {");
        for (const auto& value : e->body->values)
        {
            WriteIndent("case ." + *value->name + ": return ");
            if (value->value)
            {
                if (value->value->constant && !value->value->constant->empty())
                {
                    index = 0;
                    last = ConvertEnumConstant(enum_type, enum_type, *value->value->constant, false);
                    Write(last + " + " + std::to_string(index++));
                }
                else if (value->value->reference && !value->value->reference->empty())
                {
                    index = 0;
                    last = ConvertEnumConstant(enum_type, "", *value->value->reference, false);
                    Write(last);
                }
            }
            else
                Write(last + " + " + std::to_string(index++));
            WriteLine();
        }
        WriteLineIndent("}");
        Indent(-1);
        WriteLineIndent("}");
        WriteLine();
    }

    // Generate enum constructors
    if ((enum_type == "char") || (enum_type == "wchar"))
        WriteLineIndent("init(value: Character) { self = " + enum_name + "(rawValue: NSNumber(value: Int(String(value))!)" + enum_to + " ) }");
    if (IsUnsignedType(enum_type))
    {
        WriteLineIndent("init(value: UInt8) { self = " + enum_name + "(rawValue: NSNumber(value: value)" + enum_to + ") }");
        WriteLineIndent("init(value: UInt16) { self = " + enum_name + "(rawValue: NSNumber(value: value)" + enum_to + ") }");
        WriteLineIndent("init(value: UInt32) { self = " + enum_name + "(rawValue: NSNumber(value: value)" + enum_to + ") }");
        WriteLineIndent("init(value: UInt64) { self = " + enum_name + "(rawValue: NSNumber(value: value)" + enum_to + ") }");
    }
    else
    {
      WriteLineIndent("init(value: Int8) { self = " + enum_name + "(rawValue: NSNumber(value: value)" + enum_to + ") }");
      WriteLineIndent("init(value: Int16) { self = " + enum_name + "(rawValue: NSNumber(value: value)" + enum_to + ") }");
      WriteLineIndent("init(value: Int32) { self = " + enum_name + "(rawValue: NSNumber(value: value)" + enum_to + ") }");
      WriteLineIndent("init(value: Int64) { self = " + enum_name + "(rawValue: NSNumber(value: value)" + enum_to + ") }");
    }
    WriteLineIndent("init(value: " + enum_name + ") { self = " + enum_name + "(rawValue: value.rawValue) }");
    WriteLineIndent("init(rawValue: " + enum_mapping_type + ") { self = Self.mapValue(value: rawValue)! }");

    // Generate enum description method
    WriteLine();
    WriteLineIndent("var description: String {");
    Indent(1);
    WriteLineIndent("switch self {");
    if (e->body)
    {
        for (const auto& value : e->body->values)
            WriteLineIndent("case ." + *value->name + ":" + " return \"" + *value->name + "\"");
    }
    WriteLineIndent("}");
    Indent(-1);
    WriteLineIndent("}");

    WriteLine();
    WriteLineIndent("static let rawValuesMap: [RawValue: " + enum_name + "] = {");
    Indent(1);
    WriteLineIndent("var value = [RawValue: " + enum_name + "]()");
    for (const auto& value : e->body->values)
        WriteLineIndent("value[" + enum_name + "." + *value->name + ".rawValue] = ." + *value->name);
    WriteLineIndent("return value");
    Indent(-1);
    WriteLineIndent("}()");

    // Generate enum mapping
    WriteLine();
    WriteLineIndent("static func mapValue(value: " + enum_mapping_type + ") -> " + enum_name + "? {");
    Indent(1);
    WriteLineIndent("return rawValuesMap[value]");
    Indent(-1);
    WriteLineIndent("}");

    Indent(-1);
    WriteLineIndent("}");

    // Generate enum footer
    GenerateFooter();

    // Store the output file
    WriteEnd();
    Store(output);

    // Generate enum wrapper class
    GenerateEnumClass(p, e, path);

    std::string domain = ConvertDomain(p);
    std::string package = ConvertPackage(p);

    // Generate enum field model
    GenerateFBEFieldModelEnumFlags(domain, package, *e->name, enum_type);

    // Generate enum final model
    if (Final())
        GenerateFBEFinalModelEnumFlags(domain, package, *e->name, enum_type);
}

void GeneratorSwift::GenerateEnumClass(const std::shared_ptr<Package>& p, const std::shared_ptr<EnumType>& e, const CppCommon::Path& path)
{
    std::string enum_name = *e->name;
    std::string enum_type_name = *e->name + "Enum";

    // Generate the output file
    CppCommon::Path output = path / (enum_name + ".swift");
    WriteBegin();

    // Generate enum class header
    GenerateHeader(CppCommon::Path(_input).filename().string());
    GenerateImports("", "Foundation");

    std::string enum_type = (e->base && !e->base->empty()) ? *e->base : "int32";
    std::string enum_base_type = ConvertEnumType(enum_type);
    std::string enum_to = ConvertEnumTo(enum_type);

    // Generate enum class body
    WriteLine();
    if (JSON())
        WriteLineIndent("public struct " + enum_name + ": Comparable, Hashable, Codable {");
    else
        WriteLineIndent("public struct " + enum_name + ": Comparable, Hashable {");
    Indent(1);
    WriteLineIndent("typealias RawValue = " + enum_base_type);

    if (e->body)
    {
        for (const auto& value : e->body->values)
            WriteLineIndent("public static let " + *value->name + " = " + enum_name + "(value: " + enum_type_name + "." + *value->name + ")");
        WriteLine();
    }

    // Generate enum class value
    WriteLineIndent("var value: " + enum_type_name + "?");
    WriteLine();

    // Generate enum raw value
    WriteLineIndent("public var raw: " + enum_base_type + " { return value!.rawValue }");
    WriteLine();

    // Generate enum class constructors
    WriteLineIndent("public init() { setDefault() }");
    WriteLineIndent("public init(value: " + enum_base_type + ") { setEnum(value: value) }");
    WriteLineIndent("public init(value: " + enum_type_name + ") { setEnum(value: value) }");
    WriteLineIndent("public init(value: " + enum_name + ") { setEnum(value: value) }");
    WriteLine();

    if (JSON())
    {
        WriteLineIndent("public init(from decoder: Decoder) throws {");
        Indent(1);
        WriteLineIndent("let container = try decoder.singleValueContainer()");
        WriteLineIndent("setEnum(value: try container.decode(RawValue.self))");
        Indent(-1);
        WriteLineIndent("}");
    }

    // Generate enum class setDefault() method
    WriteLineIndent("public mutating func setDefault() { setEnum(value: NSNumber(value: 0)" + enum_to + ") }");
    WriteLine();

    // Generate enum class setEnum() methods
    WriteLineIndent("public mutating func setEnum(value: " + enum_base_type + ") { self.value = " + enum_type_name + ".mapValue(value: value) }");
    WriteLineIndent("public mutating func setEnum(value: " + enum_type_name + ") { self.value = value }");
    WriteLineIndent("public mutating func setEnum(value: " + enum_name + ") { self.value = value.value }");

    // Generate enum class compareTo() method
    WriteLine();
    WriteLineIndent("public static func < (lhs: " + enum_name + ", rhs: " + enum_name + ") -> Bool {");
    Indent(1);
    WriteLineIndent("guard let lhsValue = lhs.value, let rhsValue = rhs.value else {");
    Indent(1);
    WriteLineIndent("return false");
    WriteLineIndent("}");
    Indent(-1);
    WriteLineIndent("return lhsValue.rawValue < rhsValue.rawValue");
    Indent(-1);
    WriteLineIndent("}");

    // Generate enum class equals() method
    // Generate enum class compareTo() method
    WriteLine();
    WriteLineIndent("public static func == (lhs: " + enum_name + ", rhs: " + enum_name + ") -> Bool {");
    Indent(1);
    WriteLineIndent("guard let lhsValue = lhs.value, let rhsValue = rhs.value else {");
    Indent(1);
    WriteLineIndent("return false");
    WriteLineIndent("}");
    Indent(-1);
    WriteLineIndent("return lhsValue.rawValue == rhsValue.rawValue");
    Indent(-1);
    WriteLineIndent("}");

    // Generate enum class hashCode() method
    WriteLine();
    WriteLineIndent("public func hash(into hasher: inout Hasher) {");
    Indent(1);
    WriteLineIndent("hasher.combine(value?.rawValue ?? 0)");
    Indent(-1);
    WriteLineIndent("}");

    // Generate enum class description method
    WriteLine();
    WriteLineIndent("public var description: String {");
    Indent(1);
    WriteLineIndent("return value?.description ?? \"<unknown>\"");
    Indent(-1);
    WriteLineIndent("}");

    if (JSON())
    {
        WriteLineIndent("public func encode(to encoder: Encoder) throws {");
        Indent(1);
        WriteLineIndent("var container = encoder.singleValueContainer()");
        WriteLineIndent("try container.encode(raw)");
        Indent(-1);
        WriteLineIndent("}");

        WriteLine();
        WriteLineIndent("public func toJson() throws -> String {");
        Indent(1);
        WriteLineIndent("return String(data: try JSONEncoder().encode(self), encoding: .utf8)!");
        Indent(-1);
        WriteLineIndent("}");

        WriteLine();
        WriteLineIndent("public static func fromJson(_ json: String) throws -> " + enum_name + " {");
        Indent(1);
        WriteLineIndent("return try JSONDecoder().decode(" + enum_name + ".self, from: json.data(using: .utf8)!)");
        Indent(-1);
        WriteLineIndent("}");
    }

    Indent(-1);
    WriteLineIndent("}");

    // Generate enum class footer
    GenerateFooter();

    // Store the output file
    WriteEnd();
    Store(output);
}

void GeneratorSwift::GenerateFlags(const std::shared_ptr<Package>& p, const std::shared_ptr<FlagsType>& f, const CppCommon::Path& path)
{
    std::string flags_name = *f->name + "Enum";

    // Generate the output file
    CppCommon::Path output = path / (flags_name + ".swift");
    WriteBegin();

    // Generate flags header
    GenerateHeader(CppCommon::Path(_input).filename().string());
    GenerateImports("", "Foundation");

    std::string flags_type = (f->base && !f->base->empty()) ? *f->base : "int32";
    std::string flags_base_type = ConvertEnumType(flags_type);
    std::string flags_mapping_type = ConvertEnumBase(flags_type);
    std::string flags_int = ConvertEnumFlags(flags_type);
    std::string flags_to = ConvertEnumTo(flags_type);

    // Generate flags body
    WriteLine();
    WriteLineIndent("public struct " + flags_name + ": OptionSet {");
    Indent(1);
    if (f->body)
    {
        for (const auto& value : f->body->values)
        {
            WriteIndent("public static let " + *value->name + " = " + flags_name + "(rawValue: ");
            if (value->value)
            {
                if (value->value->constant && !value->value->constant->empty())
                    Write(ConvertEnumConstant(flags_type, flags_type, *value->value->constant, true));
                else if (value->value->reference && !value->value->reference->empty())
                    Write(ConvertEnumConstant(flags_type, "", *value->value->reference, true));
            }
            Write(")");
            WriteLine();
        }
        WriteLine();
    }
    else
        WriteIndent("public static let unknown = " + flags_name + "(rawValue: 0");

    // Generate flags class value
    WriteLineIndent("public var rawValue: " + flags_mapping_type);

    // Generate flags class constructors
    WriteLine();
    WriteLineIndent("public init(rawValue: " + flags_mapping_type + ") { self.rawValue = rawValue }");
    if ((flags_type == "char") || (flags_type == "wchar"))
        WriteLineIndent("public init(value: Char) { self.rawValue = value" + flags_to + " }");
    if (IsUnsignedType(flags_type))
    {
        WriteLineIndent("public init(value: UInt8) { self.rawValue = NSNumber(value: value)" + flags_to + " }");
        WriteLineIndent("public init(value: UInt16) { self.rawValue = NSNumber(value: value)" + flags_to + " }");
        WriteLineIndent("public init(value: UInt32) { self.rawValue = NSNumber(value: value)" + flags_to + " }");
        WriteLineIndent("public init(value: UInt64) { self.rawValue = NSNumber(value: value)" + flags_to + " }");
    }
    else
    {
        WriteLineIndent("public init(value: Int8) { self.rawValue = NSNumber(value: value)" + flags_to + " }");
        WriteLineIndent("public init(value: Int16) { self.rawValue = NSNumber(value: value)" + flags_to + " }");
        WriteLineIndent("public init(value: Int32) { self.rawValue = NSNumber(value: value)" + flags_to + " }");
        WriteLineIndent("public init(value: Int64) { self.rawValue = NSNumber(value: value)" + flags_to + " }");
    }
    WriteLineIndent("public init(value: " + flags_name + ") { self.rawValue = value.rawValue }");

    // Generate flags hasFlags() methods
    WriteLine();
    WriteLineIndent("public func hasFlags(flags: " + flags_base_type + ") -> Bool { return ((NSNumber(value: rawValue)" + flags_int + " & NSNumber(value: flags)" + flags_int + ") != " + ConvertEnumConstant(flags_type, flags_type, "0", false) + ") && (NSNumber(value: rawValue)" + flags_int + " & NSNumber(value: flags)" + flags_int + " == NSNumber(value: flags)" + flags_int + ") }");
    WriteLineIndent("public func hasFlags(flags: " + flags_name + ") -> Bool { return hasFlags(flags: flags.rawValue) }");

    // Generate flags getAllSet(), getNoneSet(), getCurrentSet() methods
    WriteLine();
    WriteLineIndent("public static let allSet: " + flags_name + " = [");
    Indent(1);
    if (f->body)
    {
        for (const auto& value : f->body->values)
        {
            WriteIndent("." + *value->name + ",");
            WriteLine();
        }
    }
    else
        WriteIndent(".unknown");
    Indent(-1);
    WriteLineIndent("]");
    WriteLineIndent("public static let noneSet: " + flags_name + " = []");
    WriteLineIndent("public var currentSet: " + flags_name + " {");
    Indent(1);
    WriteLineIndent("var result = " + flags_name + ".noneSet");
    if (f->body)
    {
        for (const auto& value : f->body->values)
        {
            WriteLineIndent("if (NSNumber(value: rawValue)" + flags_int + " & NSNumber(value: " + flags_name + "." + *value->name + ".rawValue)" + flags_int + ") != " + ConvertEnumConstant(flags_type, flags_type, "0", false) + " {");
            Indent(1);
            WriteLineIndent("result = result.union(." + *value->name + ")");
            Indent(-1);
            WriteLineIndent("}");
        }
    }
    WriteLineIndent("return result");
    Indent(-1);
    WriteLineIndent("}");

    // Generate enum description method
    WriteLine();
    WriteLineIndent("public var description: String {");
    Indent(1);
    WriteLineIndent("var sb = String()");
    if (f->body && !f->body->values.empty())
    {
        WriteLineIndent("var first = true");
        for (const auto& value : f->body->values)
        {
            WriteLineIndent("if hasFlags(flags: ." + *value->name + ") {");
            Indent(1);
            WriteLineIndent("sb.append(first ? \"\" : \"|\"); sb.append(\"" + *value->name + "\")");
            WriteLineIndent("first = false");
            Indent(-1);
            WriteLineIndent("}");
        }
    }
    WriteLineIndent("return sb");
    Indent(-1);
    WriteLineIndent("}");

    // Generate flags values() method
    WriteLine();
    WriteLineIndent("static let rawValuesMap: [RawValue: " + flags_name + "] = {");
    Indent(1);
    WriteLineIndent("var value = [RawValue: " + flags_name + "]()");
    if (f->body)
    {
        for (const auto& value : f->body->values)
        {
            WriteIndent("value[" + flags_name + "." + *value->name + ".rawValue] = ." + *value->name);
            WriteLine();
        }
    }
    else
        WriteIndent("value[" +flags_name + ".unknown.rawValue] = .unknown");
    WriteLineIndent("return value");
    Indent(-1);
    WriteLineIndent("}()");

    // Generate flags mapValue() method
    WriteLine();
    WriteLineIndent("public static func mapValue(value: RawValue) -> " + flags_name + "? {");
    Indent(1);
    WriteLineIndent("return rawValuesMap[value]");
    Indent(-1);
    WriteLineIndent("}");

    Indent(-1);
    WriteLineIndent("}");

    // Generate flags footer
    GenerateFooter();

    // Store the output file
    WriteEnd();
    Store(output);

    // Generate flags wrapper class
    GenerateFlagsClass(p, f, path);

    std::string domain = ConvertDomain(p);
    std::string package = ConvertPackage(p);

    // Generate flags field model
    GenerateFBEFieldModelEnumFlags(domain, package, *f->name, flags_type);

    // Generate flags final model
    if (Final())
        GenerateFBEFinalModelEnumFlags(domain, package, *f->name, flags_type);
}

void GeneratorSwift::GenerateFlagsClass(const std::shared_ptr<Package>& p, const std::shared_ptr<FlagsType>& f, const CppCommon::Path& path)
{
    std::string flags_name = *f->name;
    std::string flags_type_name = *f->name + "Enum";

    // Generate the output file
    CppCommon::Path output = path / (flags_name + ".swift");
    WriteBegin();

    // Generate flags class header
    GenerateHeader(CppCommon::Path(_input).filename().string());
    GenerateImports("", "Foundation");

    std::string flags_type = (f->base && !f->base->empty()) ? *f->base : "int32";
    std::string flags_base_type = ConvertEnumType(flags_type);
    std::string flags_int = ConvertEnumFlags(flags_type);
    std::string flags_to = ConvertEnumTo(flags_type);

    // Generate flags class body
    WriteLine();
    if (JSON())
        WriteLineIndent("public struct " + flags_name + ": Comparable, Hashable, Codable {");
    else
        WriteLineIndent("public struct " + flags_name + ": Comparable, Hashable {");
    Indent(1);
    WriteLineIndent("typealias RawValue = " + flags_base_type);
    if (f->body)
    {
        if (!f->body->values.empty())
        {
            for (const auto& value : f->body->values)
                WriteLineIndent("public static let " + *value->name + " = " + flags_name + "(value: ." + *value->name + ")");
            WriteLine();
        }

        // Generate flags class fromSet() method
        WriteLineIndent("public static func fromSet(set: " + flags_type_name + ") -> " + flags_name + " {");
        Indent(1);
        WriteLineIndent("var result = NSNumber(value: " + ConvertEnumConstant(flags_type, flags_type, "0", false) + ")" + flags_int);
        if (f->body)
        {
            for (const auto& value : f->body->values)
            {
                WriteLineIndent("if set.contains(" + flags_name + "." + *value->name + ".value!) {");
                Indent(1);
                WriteLineIndent("result = NSNumber(value: result)" + flags_int + " | NSNumber(value: " +flags_name + "." + *value->name + ".raw)" + flags_int);
                Indent(-1);
                WriteLineIndent("}");
            }
        }
        WriteLineIndent("return " + flags_name + "(value: NSNumber(value: result)" + flags_to + ")");
        Indent(-1);
        WriteLineIndent("}");

        WriteLine();
    }

    // Generate flags class value
    WriteLineIndent("public private(set) var value: " + flags_type_name + "?");
    WriteLine();

    // Generate flags raw value
    WriteLineIndent("public private(set) var raw: " + flags_base_type + " = 0");
    WriteLine();

    // Generate flags class constructors
    WriteLineIndent("public init() { setDefaults() }");
    WriteLineIndent("public init(value: " + flags_base_type + ") { setEnum(value: value) }");
    WriteLineIndent("public init(value: " + flags_type_name + ") { setEnum(value: value) }");
    WriteLineIndent("public init(value: " + flags_name + ") { setEnum(value: value) }");

    if (JSON())
    {
        WriteLine();
        WriteLineIndent("public init(from decoder: Decoder) throws {");
        Indent(1);
        WriteLineIndent("let container = try decoder.singleValueContainer()");
        WriteLineIndent("setEnum(value: try container.decode(RawValue.self))");
        Indent(-1);
        WriteLineIndent("}");
    }

    // Generate flags class setDefault() method
    WriteLine();
    WriteLineIndent("public mutating func setDefaults() { setEnum(value: 0) }");
    WriteLine();

    // Generate flags class setEnum() methods
    WriteLineIndent("public mutating func setEnum(value: " + flags_base_type + ") { self.raw = value; self.value = " + flags_type_name + ".mapValue(value: value) }");
    WriteLineIndent("public mutating func setEnum(value: " + flags_type_name + ") { self.raw = value.rawValue; self.value = value }");
    WriteLineIndent("public mutating func setEnum(value: " + flags_name + ") { self.raw = value.raw; self.value = value.value }");

    // Generate flags class hasFlags() methods
    WriteLine();
    WriteLineIndent("public func hasFlags(flags: " + flags_base_type + ") -> Bool { return (NSNumber(value: raw)" + flags_int + " & NSNumber(value: flags)" + flags_int + " != " + ConvertEnumConstant(flags_type, flags_type, "0", false) + ") && ((NSNumber(value: raw)" + flags_int + " & NSNumber(value: flags)" + flags_int + ") == NSNumber(value: flags)" + flags_int + ") }");
    WriteLineIndent("public func hasFlags(flags: " + flags_type_name + ") -> Bool { return hasFlags(flags: flags.rawValue) }");
    WriteLineIndent("public func hasFlags(flags: " + flags_name + ") -> Bool { return hasFlags(flags: flags.raw) }");

    // Generate flags class setFlags() methods
    WriteLine();
    WriteLineIndent("public mutating func setFlags(flags: " + flags_base_type + ") -> " + flags_name + " { setEnum(value: NSNumber(value: NSNumber(value: raw)" + flags_int + " | NSNumber(value: flags)" + flags_int + ")" + flags_to + "); return self }");
    WriteLineIndent("public mutating func setFlags(flags: " + flags_type_name + ") -> " + flags_name + " { _ = setFlags(flags: flags.rawValue); return self }");
    WriteLineIndent("public mutating func setFlags(flags: " + flags_name + ") -> " + flags_name + " { _ = setFlags(flags: flags.raw); return self }");

    // Generate flags class removeFlags() methods
    WriteLine();
    WriteLineIndent("public mutating func removeFlags(flags: " + flags_base_type + ") -> " + flags_name + " { setEnum(value: NSNumber(value: NSNumber(value: raw)" + flags_int + " | NSNumber(value: flags)" + flags_int + ".byteSwapped)" + flags_to + "); return self }");
    WriteLineIndent("public mutating func removeFlags(flags: " + flags_type_name + ") -> " + flags_name + " { _ = removeFlags(flags: flags.rawValue); return self }");
    WriteLineIndent("public mutating func removeFlags(flags: " + flags_name + ") -> " + flags_name + " { _ = removeFlags(flags: flags.raw); return self }");

    // Generate flags class getAllSet(), getNoneSet() and getCurrentSet() methods
    WriteLine();
    WriteLineIndent("public var allSet: " + flags_type_name + " { return .allSet }");
    WriteLineIndent("public var noneSet: " + flags_type_name + " { return .noneSet }");
    WriteLineIndent("public var currentSet: " + flags_type_name + " { return value!.currentSet }");

    // Generate flags class < method
    WriteLine();
    WriteLineIndent("public static func < (lhs: " + flags_name + ", rhs: " + flags_name + ") -> Bool {");
    Indent(1);
    WriteLineIndent("return lhs.raw < rhs.raw");
    Indent(-1);
    WriteLineIndent("}");

    // Generate flags class == method
    WriteLine();
    WriteLineIndent("public static func == (lhs: " + flags_name + ", rhs: " + flags_name + ") -> Bool {");
    Indent(1);
    WriteLineIndent("return lhs.raw == rhs.raw");
    Indent(-1);
    WriteLineIndent("}");

    // Generate flags class hashCode() method
    WriteLine();
    WriteLineIndent("public func hash(into hasher: inout Hasher) {");
    Indent(1);
    WriteLineIndent("hasher.combine(raw)");
    Indent(-1);
    WriteLineIndent("}");

    // Generate flags class description method
    WriteLine();
    WriteLineIndent("public var description: String {");
    Indent(1);
    WriteLineIndent("var sb = String()");
    if (f->body && !f->body->values.empty())
    {
        WriteLineIndent("var first = true");
        for (const auto& value : f->body->values)
        {
            WriteLineIndent("if hasFlags(flags: " + flags_name + "." + *value->name + ".raw) {");
            Indent(1);
            WriteLineIndent("sb.append(first ? \"\" : \"|\"); sb.append(\"" + *value->name + "\")");
            WriteLineIndent("first = false");
            Indent(-1);
            WriteLineIndent("}");
        }
    }
    WriteLineIndent("return sb");
    Indent(-1);
    WriteLineIndent("}");

    if (JSON())
    {
        WriteLineIndent("public func encode(to encoder: Encoder) throws {");
        Indent(1);
        WriteLineIndent("var container = encoder.singleValueContainer()");
        WriteLineIndent("try container.encode(raw)");
        Indent(-1);
        WriteLineIndent("}");

        WriteLine();
        WriteLineIndent("public func toJson() throws -> String {");
        Indent(1);
        WriteLineIndent("return String(data: try JSONEncoder().encode(self), encoding: .utf8)!");
        Indent(-1);
        WriteLineIndent("}");

        WriteLine();
        WriteLineIndent("public static func fromJson(_ json: String) throws -> " + flags_name + " {");
        Indent(1);
        WriteLineIndent("return try JSONDecoder().decode(" + flags_name + ".self, from: json.data(using: .utf8)!)");
        Indent(-1);
        WriteLineIndent("}");
    }

    Indent(-1);
    WriteLineIndent("}");

    // Generate flags class footer
    GenerateFooter();

    // Store the output file
    WriteEnd();
    Store(output);
}

void GeneratorSwift::GenerateStruct(const std::shared_ptr<Package>& p, const std::shared_ptr<StructType>& s, const CppCommon::Path& path)
{
    std::string domain = ConvertDomain(p);
    std::string package = ConvertPackage(p);
    bool first;

    // Generate the output file
    CppCommon::Path output = path / (*s->name + ".swift");
    WriteBegin();

    // Generate struct header
    GenerateHeader(CppCommon::Path(_input).filename().string());
    GenerateImports("", "Foundation");
    GenerateImports(domain, "Fbe");
    GenerateImports(p);

    // Generate based protocol

    WriteLine();
    WriteIndent("public protocol " + *s->name + "Base");
    if (s->base && !s->base->empty())
         Write(": " + ConvertTypeName(domain, "", ConvertPackageName(*s->base), false) + "Base");
    WriteLineIndent(" {");
    Indent(1);
    if (s->body && !s->body->fields.empty())
    {
        for (const auto& field : s->body->fields)
        {
            std::string fieldName = (*field->name == "Type") ? ("`Type`") : *field->name;
            WriteLineIndent("var " + fieldName + ": " + ConvertTypeName(domain, "", *field, false) + " { get set }");
        }
    }
    Indent(-1);
    WriteLineIndent("}");

    WriteLine();
    WriteIndent("public protocol " + *s->name + "Inheritance");
    if (s->base && !s->base->empty())
         Write(": " + ConvertTypeName(domain, "", ConvertPackageName(*s->base), false) + "Inheritance");
    WriteLineIndent(" {");
    Indent(1);
    WriteLineIndent("var parent: " + *s->name + " { get set }");
    Indent(-1);
    WriteLineIndent("}");


    WriteLine();
    WriteLineIndent("extension " + *s->name + "Inheritance {");
    Indent(1);
    // Generate struct body

    if (s->base && !s->base->empty())
    {
        WriteLineIndent("public var parent: " + ConvertTypeName(domain, "", ConvertPackageName(*s->base), false) + " {");
        Indent(1);
        WriteLineIndent("get { return parent.parent }");
        WriteLineIndent("set { parent.parent = newValue }");
        Indent(-1);
        WriteLineIndent("}");
    }
    if (s->body && !s->body->fields.empty())
    {
        for (const auto& field : s->body->fields)
        {
            std::string fieldName = (*field->name == "Type") ? ("`Type`") : *field->name;
            WriteLineIndent("public var " + fieldName + ": " + ConvertTypeName(domain, "", *field, false) + " {");
            Indent(1);
            WriteLineIndent("get { return parent." + fieldName + " }");
            WriteLineIndent("set { parent." + fieldName + " = newValue }");
            Indent(-1);
            WriteLineIndent("}");
        }
    }
    Indent(-1);
    WriteLineIndent("}");


    // Generate struct begin
    WriteLine();
    WriteIndent("public struct " + *s->name + ": " + *s->name + "Base");
    if (s->base && !s->base->empty())
         Write(", " + ConvertTypeName(domain, "", ConvertPackageName(*s->base), false) + "Inheritance");
    if (JSON())
        Write(", Comparable, Hashable, Codable");
    else
        Write(", Comparable, Hashable");
    WriteLineIndent(" {");
    Indent(1);

    // Generate parent struct
    if (s->base && !s->base->empty())
        WriteLineIndent("public var parent: " + ConvertTypeName(domain, "", ConvertPackageName(*s->base), false));

    // Generate struct body
    if (s->body && !s->body->fields.empty())
    {
        for (const auto& field : s->body->fields)
        {
            std::string fieldName = (*field->name == "Type") ? ("`Type`") : *field->name;
            WriteLineIndent("public var " + fieldName + ": " + ConvertTypeName(domain, "", *field, false) + " = " + ConvertDefault(domain, package, *field));
        }
    }

    WriteLine();
    // Generate struct default constructor
    if (s->base && !s->base->empty())
        WriteLineIndent("public init() { parent = " + ConvertTypeName(domain, "", ConvertPackageName(*s->base), false) + "() }");
    else
        WriteLineIndent("public init() { }");

    // Generate struct initialization constructor
    if ((s->base && !s->base->empty()) || (s->body && !s->body->fields.empty()))
    {
        first = true;
        WriteIndent("public init(");
        if (s->base && !s->base->empty())
        {
            Write("parent: " + ConvertTypeName(domain, "", ConvertPackageName(*s->base), false));
            first = false;
        }
        if (s->body)
        {
            for (const auto& field : s->body->fields)
            {
                Write(std::string(first ? "" : ", ") + *field->name + ": " + ConvertTypeName(domain, "", *field, false));
                first = false;
            }
        }
        Write(") {");
        WriteLine();
        Indent(1);
        if (s->base && !s->base->empty())
            WriteLineIndent("self.parent = parent");
        WriteLine();
        if (s->body)
            for (const auto& field : s->body->fields)
                WriteLineIndent("self." + *field->name + " = " + *field->name);
        Indent(-1);
        WriteLineIndent("}");
    }

    // Generate struct copy constructor
    WriteLine();
    WriteLineIndent("public init(other: " + *s->name + ") {");
    Indent(1);
    if (s->base && !s->base->empty())
        WriteLineIndent("parent = other.parent");
    if (s->body)
        for (const auto& field : s->body->fields)
            WriteLineIndent("self." + *field->name + " = other." + *field->name);
    Indent(-1);
    WriteLineIndent("}");

    // Generate struct json constructor
    if (JSON())
    {
        WriteLine();
        WriteLineIndent("public init(from decoder: Decoder) throws {");
        Indent(1);
        if (s->base && !s->base->empty())
            WriteLineIndent("parent = try " + ConvertTypeName(domain, "", ConvertPackageName(*s->base), false) + "(from: decoder)");

        WriteLineIndent("let container = try decoder.container(keyedBy: CodingKeys.self)");
        if (s->body)
        {
            for (const auto& field : s->body->fields)
            {
                if ((field->vector || field->list || field->set || field->map || field->hash) && *field->type == "timestamp")
                {
                    WriteLineIndent(*field->name + " = Dictionary(uniqueKeysWithValues: (try container.decode(Dictionary<String, " + ConvertTypeName(domain, "", "TimeInterval", field->optional) + ">.self, forKey: ." + *field->name + ")).map { (" + ConvertTypeName(domain, "", *field->key, false) + "($0) ?? 0, Date(timeIntervalSince1970: $1 / 1_000_000_000)) })");
                }
                else if (field->map && *field->key != "string")
                {
                    WriteLineIndent(*field->name + " = Dictionary(uniqueKeysWithValues: (try container.decode(Dictionary<String, " + ConvertTypeName(domain, "", *field->type, field->optional) + ">.self, forKey: ." + *field->name + ")).map { (" + ConvertTypeName(domain, "", *field->key, false) + "($0) ?? 0, $1) })");
                }
                else if (*field->type == "decimal")
                {
                    if (field->optional)
                    {
                        WriteLineIndent("let " + *field->name + "RawValue = try container.decode(String?.self, forKey: ." + *field->name + ")");
                        WriteLineIndent(*field->name + " = " + *field->name + "RawValue != nil ? Decimal(string: " + *field->name + "RawValue!) ?? .nan : nil");
                    } else
                        WriteLineIndent(*field->name + " = Decimal(string: try container.decode(String.self, forKey: ." + *field->name + ")) ?? .nan");
                }
                else if (*field->type == "timestamp")
                {
                    if (field->optional)
                    {
                        WriteLineIndent("let " + *field->name + "RawValue = try container.decode(TimeInterval?.self, forKey: ." + *field->name + ")");
                        WriteLineIndent(*field->name + " = " + *field->name + "RawValue != nil ? Date(timeIntervalSince1970: " + *field->name + "RawValue! / 1_000_000_000) : nil");
                    } else
                        WriteLineIndent(*field->name + " = Date(timeIntervalSince1970: try container.decode(TimeInterval.self, forKey: ." + *field->name + ") / 1_000_000_000)");
                }
                else if ((*field->type == "char") || (*field->type == "wchar"))
                {
                    std::string valueType = (*field->type == "char") ? "UInt8" : "UInt32";
                    std::string valueOpt = (*field->type == "char") ? "" : "!";
                    if (field->optional)
                    {
                        WriteLineIndent("let " + *field->name + "RawValue: " + valueType + "? = try container.decode(" + valueType + "?.self, forKey: ." + *field->name + ")");
                        WriteLineIndent(*field->name + " = " + *field->name + "RawValue != nil ? Character(UnicodeScalar(" + *field->name + "RawValue!)" + valueOpt + ") : nil");
                    } else {
                        WriteLineIndent("let " + *field->name + "RawValue: " + valueType + " = try container.decode(" + valueType + ".self, forKey: ." + *field->name + ")");
                        WriteLineIndent(*field->name + " = Character(UnicodeScalar(" + *field->name + "RawValue)" + valueOpt + ")");
                    }
                }
                else
                    WriteLineIndent(*field->name + " = try container.decode(" + ConvertTypeName(domain, package, *field, false) + ".self, forKey: ." + *field->name + ")");
            }
        }

        Indent(-1);
    WriteLineIndent("}");
    }

    // Generate struct clone() method
    WriteLine();
    WriteLineIndent("public func clone() throws -> " + *s->name + " {");
    Indent(1);
    WriteLineIndent("// Serialize the struct to the FBE stream");
    WriteLineIndent("let writer = " + *s->name + "Model()");
    WriteLineIndent("try _ = writer.serialize(value: self)");
    WriteLine();
    WriteLineIndent("// Deserialize the struct from the FBE stream");
    WriteLineIndent("let reader = " + *s->name + "Model()");
    WriteLineIndent("reader.attach(buffer: writer.buffer)");
    WriteLineIndent("return reader.deserialize()");
    Indent(-1);
    WriteLineIndent("}");

    // Generate struct compareTo() method
    WriteLine();
    WriteLineIndent("public static func < (lhs: " + *s->name + ", rhs: " + *s->name + ") -> Bool {");
    Indent(1);
    if (s->body)
        for (const auto& field : s->body->fields)
            if (field->keys)
                WriteLineIndent("if !(lhs." + *field->name + " < rhs." + *field->name + ") { return false }");
    WriteLineIndent("return true");
    Indent(-1);
    WriteLineIndent("}");

    // Generate struct equals() method
    WriteLine();
    WriteLineIndent("public static func == (lhs: " + *s->name + ", rhs: " + *s->name + ") -> Bool {");
    Indent(1);
    if (s->body)
        for (const auto& field : s->body->fields)
            if (field->keys)
                WriteLineIndent("if !(lhs." + *field->name + " == rhs." + *field->name + ") { return false }");
    WriteLineIndent("return true");
    Indent(-1);
    WriteLineIndent("}");

    // Generate struct hashCode() method
    WriteLine();
    WriteLineIndent("public func hash(into hasher: inout Hasher) {");
    Indent(1);
    if (s->base && !s->base->empty())
        WriteLineIndent("parent.hash(into: &hasher)");
    if (s->body)
        for (const auto& field : s->body->fields)
            if (field->keys)
                WriteLineIndent("hasher.combine(" + *field->name + ")");
    Indent(-1);
    WriteLineIndent("}");

    // Generate struct description method
    WriteLine();
    WriteLineIndent("public var description: String {");
    Indent(1);
    WriteLineIndent("var sb = String()");
    WriteLineIndent("sb.append(\"" + *s->name + "(\")");
    first = true;
    if (s->base && !s->base->empty())
    {
        WriteLineIndent("sb.append(parent.description)");
        first = false;
    }
    if (s->body)
    {
        for (const auto& field : s->body->fields)
        {
            if (field->attributes && field->attributes->hidden)
                WriteLineIndent("sb.append(\"" + std::string(first ? "" : ",") + *field->name + "=***\");");
            else if (field->array || field->vector)
            {
                WriteLineIndent("if true {");
                Indent(1);
                WriteLineIndent("var first = true");
                WriteLineIndent("sb.append(\"" + std::string(first ? "" : ",") + *field->name + "=[\"); sb.append(\"\\(" + *field->name + ".count)\"" + "); sb.append(\"][\")");
                WriteLineIndent("for item in " + *field->name + " {");
                Indent(1);
                WriteLineIndent(ConvertOutputStreamValue(*field->type, "item", field->optional, true, false));
                WriteLineIndent("first = false");
                Indent(-1);
                WriteLineIndent("}");
                WriteLineIndent("sb.append(\"]\")");
                Indent(-1);
                WriteLineIndent("}");
            }
            else if (field->list)
            {
                WriteLineIndent("if true {");
                Indent(1);
                WriteLineIndent("var first = true");
                WriteLineIndent("sb.append(\"" + std::string(first ? "" : ",") + *field->name + "=[\"); sb.append(\"\\(" + *field->name + ".count)\"" + "); sb.append(\"]<\")");
                WriteLineIndent("for item in " + *field->name + " {");
                Indent(1);
                WriteLineIndent(ConvertOutputStreamValue(*field->type, "item", field->optional, true, false));
                WriteLineIndent("first = false");
                Indent(-1);
                WriteLineIndent("}");
                WriteLineIndent("sb.append(\">\")");
                Indent(-1);
                WriteLineIndent("}");
            }
            else if (field->set)
            {
                WriteLineIndent("if true {");
                Indent(1);
                WriteLineIndent("var first = true");
                WriteLineIndent("sb.append(\"" + std::string(first ? "" : ",") + *field->name + "=[\"); sb.append(\"\\(" + *field->name + ".count)\"" + "); sb.append(\"]{\")");
                WriteLineIndent("for item in " + *field->name + " {");
                Indent(1);
                WriteLineIndent(ConvertOutputStreamValue(*field->type, "item", field->optional, true, false));
                WriteLineIndent("first = false");
                Indent(-1);
                WriteLineIndent("}");
                WriteLineIndent("sb.append(\"}\")");
                Indent(-1);
                WriteLineIndent("}");
            }
            else if (field->map)
            {
                WriteLineIndent("if true {");
                Indent(1);
                WriteLineIndent("var first = true");
                WriteLineIndent("sb.append(\"" + std::string(first ? "" : ",") + *field->name + "=[\"); sb.append(" + *field->name + ".count.description); sb.append(\"]<{\")");
                WriteLineIndent("for (key, value) in " + *field->name + " {");
                Indent(1);
                WriteLineIndent(ConvertOutputStreamValue(*field->key, "key", false, true, false));
                WriteLineIndent("sb.append(\"->\")");
                WriteLineIndent(ConvertOutputStreamValue(*field->type, "value", field->optional, false, true));
                WriteLineIndent("first = false");
                Indent(-1);
                WriteLineIndent("}");
                WriteLineIndent("sb.append(\"}>\")");
                Indent(-1);
                WriteLineIndent("}");
            }
            else if (field->hash)
            {
                WriteLineIndent("if true {");
                Indent(1);
                WriteLineIndent("var first = true");
                WriteLineIndent("sb.append(\"" + std::string(first ? "" : ",") + *field->name + "=[\"); sb.append(" + *field->name + ".count.description); sb.append(\"][{\")");
                WriteLineIndent("for (key, value) in " + *field->name + " {");
                Indent(1);
                WriteLineIndent(ConvertOutputStreamValue(*field->key, "key", false, true, false));
                WriteLineIndent("sb.append(\"->\")");
                WriteLineIndent(ConvertOutputStreamValue(*field->type, "value", field->optional, false, true));
                WriteLineIndent("first = false");
                Indent(-1);
                WriteLineIndent("}");
                WriteLineIndent("sb.append(\"}]\")");
                Indent(-1);
                WriteLineIndent("}");
            }
            else
                WriteLineIndent("sb.append(\"" + std::string(first ? "" : ",") + *field->name + "=\"); " + ConvertOutputStreamValue(*field->type, *field->name, field->optional, false, true));
            first = false;
        }
    }
    WriteLineIndent("sb.append(\")\")");
    WriteLineIndent("return sb");
    Indent(-1);
    WriteLineIndent("}");

    // Generate struct JSON methods
    if (JSON())
    {
        WriteLineIndent("private enum CodingKeys: String, CodingKey {");
        Indent(1);
        if (s->body && !s->body->fields.empty())
        {
            for (const auto& field : s->body->fields)
            {
                std::string fieldName = (*field->name == "Type") ? ("`Type`") : *field->name;
                WriteLineIndent("case " + fieldName);
            }
        }
        else
            WriteLineIndent("case empty");
        Indent(-1);
        WriteLineIndent("}");

        WriteLine();
        WriteLineIndent("public func encode(to encoder: Encoder) throws {");
        Indent(1);
        if (s->base && !s->base->empty())
            WriteLineIndent("try parent.encode(to: encoder)");
        WriteLineIndent("var container = encoder.container(keyedBy: CodingKeys.self)");
        if (s->body && !s->body->fields.empty())
        {
            for (const auto& field : s->body->fields)
            {
                if ((field->vector || field->list || field->set || field->map || field->hash) && *field->type == "timestamp")
                    WriteLineIndent("try container.encode(Dictionary(uniqueKeysWithValues: " + *field->name + ".map { ($0.description, $1.timeIntervalSince1970) }), forKey: ." + *field->name + ")");
                else if (field->map && (*field->key != "string"))
                    WriteLineIndent("try container.encode(Dictionary(uniqueKeysWithValues: " + *field->name + ".map { ($0.description, $1) }), forKey: ." + *field->name + ")");
                else if (*field->type == "decimal")
                {
                    std::string opt = (field->optional) ? "?" : "";
                    WriteLineIndent("try container.encode(" + *field->name + opt + ".description, forKey: ." + *field->name + ")");
                }
                else if ((*field->type == "char") || (*field->type == "wchar"))
                {
                    std::string valueType = (*field->type == "char") ? "UInt8" : "UInt32";
                    std::string utfType = (*field->type == "char") ? "utf8" : "utf16";
                    std::string opt = (field->optional) ? "?" : "";
                    WriteLineIndent("try container.encode(" + *field->name + opt + "." + utfType + ".map { " + valueType + "($0) }[0], forKey: ." + *field->name + ")");
                }
                else if (*field->type == "timestamp")
                {
                    if (field->optional)
                        WriteLineIndent("try container.encode(" + *field->name + " != nil ? floor(" + *field->name + "!" + ".timeIntervalSince1970 * 1000) * 1_000_000 : nil" + ", forKey: ." + *field->name + ")");
                    else
                        WriteLineIndent("try container.encode(floor(" + *field->name + ".timeIntervalSince1970 * 1000) * 1_000_000" + ", forKey: ." + *field->name + ")");
                }
                else
                    WriteLineIndent("try container.encode(" + *field->name + ", forKey: ." + *field->name + ")");
            }
        }
        Indent(-1);
        WriteLineIndent("}");

        WriteLine();
        WriteLineIndent("public func toJson() throws -> String {");
        Indent(1);
        WriteLineIndent("return String(data: try JSONEncoder().encode(self), encoding: .utf8)!");
        Indent(-1);
        WriteLineIndent("}");

        WriteLine();
        WriteLineIndent("public static func fromJson(_ json: String) throws -> " + *s->name + " {");
        Indent(1);
        WriteLineIndent("return try JSONDecoder().decode(" + *s->name + ".self, from: json.data(using: .utf8)!)");
        Indent(-1);
        WriteLineIndent("}");
    }

    Indent(-1);
    WriteLineIndent("}");

    // Generate struct footer
    GenerateFooter();

    // Store the output file
    WriteEnd();
    Store(output);

    // Generate struct field models
    GenerateStructFieldModel(p, s);
    GenerateStructModel(p, s);

    // Generate struct final models
    if (Final())
    {
        GenerateStructFinalModel(p, s);
        GenerateStructModelFinal(p, s);
    }
}

void GeneratorSwift::GenerateStructFieldModel(const std::shared_ptr<Package>& p, const std::shared_ptr<StructType>& s)
{
    std::string domain = ConvertDomain(p);
    std::string package = ConvertPackage(p);
    std::string struct_name = *s->name;

    CppCommon::Path path = (CppCommon::Path(_output) / CreatePackagePath(domain, package)) / "Fbe";

    // Create package path
    CppCommon::Directory::CreateTree(path);

    // Generate the file
    CppCommon::Path file = path / ("FieldModel" + *s->name + ".swift");
    WriteBegin();

    // Generate headers
    GenerateHeader(CppCommon::Path(_input).filename().string());
    GenerateImports("", "Foundation");
    GenerateImports(domain, "Fbe");
    GenerateImports(p);

    // Generate struct field model begin
    WriteLine();
    WriteLineIndent("// Fast Binary Encoding " + *s->name + " field model");
    WriteLineIndent("public class FieldModel" + *s->name + ": FieldModel {");
    Indent(1);

    WriteLine();
    WriteLineIndent("public var _buffer: Buffer");
    WriteLineIndent("public var _offset: Int");

    // Generate struct field model accessors
    if (s->base && !s->base->empty())
    {
        WriteLine();
        WriteLineIndent("let parent: " + ConvertBaseFieldName(domain, ConvertPackageName(*s->base), false));
    }
    if (s->body && !s->body->fields.empty())
    {
        WriteLine();
        for (const auto& field : s->body->fields)
        {
            std::string fieldName = (*field->name == "Type") ? ("`Type`") : *field->name;
            WriteLineIndent("let " + fieldName + ": " + ConvertTypeFieldDeclaration(domain, *field, false));
        }
    }

    // Generate struct field model FBE properties
    WriteLine();
    WriteLineIndent("// Field size");
    WriteLineIndent("public let fbeSize: Int = 4");
    WriteLine();
    WriteLineIndent("// Field body size");
    WriteLineIndent("public let fbeBody: Int");

    // Generate struct field model init() method
    WriteLine();
    WriteLineIndent("// Set the struct value (end phase)");
    WriteLineIndent("public required init() {");
    Indent(1);
    WriteLineIndent("let buffer = Buffer()");
    WriteLineIndent("let offset = 0");
    WriteLine();
    WriteLineIndent("_buffer = buffer");
    WriteLineIndent("_offset = offset");

    // Generate struct field model accessors
    std::string prev_offset("4");
    std::string prev_size("4");
    if (s->base && !s->base->empty())
    {
        WriteLine();
        WriteLineIndent("parent = " + ConvertBaseFieldName(domain, ConvertPackageName(*s->base), false) + "(buffer: buffer, offset: " + prev_offset + " + " + prev_size + ")");
        prev_offset = "parent.fbeOffset";
        prev_size = "parent.fbeBody - 4 - 4";
    }
    if (s->body && !s->body->fields.empty())
    {
        WriteLine();
        for (const auto& field : s->body->fields)
        {
            WriteLineIndent(*field->name + " = " + ConvertTypeFieldInitialization(domain, *field, prev_offset + " + " + prev_size, false));
            prev_offset = *field->name + ".fbeOffset";
            prev_size = *field->name + ".fbeSize";
        }
    }
    WriteLine();

    if ((s->base && !s->base->empty()) || (s->body && !s->body->fields.empty()))
        WriteLineIndent("var fbeBody = (4 + 4)");
    else
        WriteLineIndent("let fbeBody = (4 + 4)");

    Indent(1);
    if (s->base && !s->base->empty())
        WriteLineIndent("fbeBody += parent.fbeBody - 4 - 4");
    if (s->body)
        for (const auto& field : s->body->fields)
            WriteLineIndent("fbeBody += " + *field->name + ".fbeSize");
    Indent(-1);
    WriteLineIndent("self.fbeBody = fbeBody");
    Indent(-1);
    WriteLineIndent("}");

    // Generate struct field model init(...) method
    WriteLine();
    WriteLineIndent("// ");
    WriteLineIndent("public required init(buffer: Buffer = Buffer(), offset: Int = 0) {");
    Indent(1);
    WriteLineIndent("_buffer = buffer");
    WriteLineIndent("_offset = offset");
    WriteLine();
    // Generate struct field model accessors
    prev_offset = "4";
    prev_size = "4";
    if (s->base && !s->base->empty())
    {
        WriteLineIndent("parent = " + ConvertBaseFieldName(domain, ConvertPackageName(*s->base), false) + "(buffer: buffer, offset: " + prev_offset + " + " + prev_size + ")");
        prev_offset = "parent.fbeOffset";
        prev_size = "parent.fbeBody - 4 - 4";
    }
    if (s->body)
    {
        for (const auto& field : s->body->fields)
        {
            WriteLineIndent(*field->name + " = " + ConvertTypeFieldInitialization(domain, *field, prev_offset + " + " + prev_size, false));
            prev_offset = *field->name + ".fbeOffset";
            prev_size = *field->name + ".fbeSize";
        }
    }
    WriteLine();
    if ((s->base && !s->base->empty()) || (s->body && !s->body->fields.empty()))
        WriteLineIndent("var fbeBody = (4 + 4)");
    else
        WriteLineIndent("let fbeBody = (4 + 4)");
    Indent(1);
    if (s->base && !s->base->empty())
        WriteLineIndent("fbeBody += parent.fbeBody - 4 - 4");
    if (s->body)
        for (const auto& field : s->body->fields)
            WriteLineIndent("fbeBody += " + *field->name + ".fbeSize");
    Indent(-1);
    WriteLineIndent("self.fbeBody = fbeBody");
    Indent(-1);
    WriteLineIndent("}");

    WriteLine();
    WriteLineIndent("// Field extra size");
    WriteLineIndent("public var fbeExtra: Int {");
    Indent(1);
    WriteLineIndent("if _buffer.offset + fbeOffset + fbeSize > _buffer.size {");
    Indent(1);
    WriteLineIndent("return 0");
    Indent(-1);
    WriteLineIndent("}");
    WriteLine();
    WriteLineIndent("let fbeStructOffset = Int(readUInt32(offset: fbeOffset))");
    WriteLineIndent("if (fbeStructOffset == 0) || ((_buffer.offset + fbeStructOffset + 4) > _buffer.size) {");
    Indent(1);
    WriteLineIndent("return 0");
    Indent(-1);
    WriteLineIndent("}");
    WriteLine();
    WriteLineIndent("_buffer.shift(offset: fbeStructOffset)");
    WriteLine();

    if ((s->base && !s->base->empty()) || (s->body && !s->body->fields.empty()))
        WriteLineIndent("var fbeResult = fbeBody");
    else
        WriteLineIndent("let fbeResult = fbeBody");
    Indent(1);
    if (s->base && !s->base->empty())
        WriteLineIndent("fbeResult += parent.fbeExtra");
    if (s->body)
        for (const auto& field : s->body->fields)
            WriteLineIndent("fbeResult += " + *field->name + ".fbeExtra");
    Indent(-1);
    WriteLine();
    WriteLineIndent("_buffer.unshift(offset: fbeStructOffset)");
    WriteLine();
    WriteLineIndent("return fbeResult");
    Indent(-1);
    WriteLineIndent("}");

    WriteLine();
    WriteLineIndent("// Field type");
    WriteLineIndent("public var fbeType: Int = fbeTypeConst");
    if (s->base && !s->base->empty() && (s->type == 0))
        WriteLineIndent("public static let fbeTypeConst: Int = " + ConvertBaseFieldName(domain, ConvertPackageName(*s->base), false) + ".fbeTypeConst");
    else
        WriteLineIndent("public static let fbeTypeConst: Int = " + std::to_string(s->type));

    // Generate struct field model verify() methods
    WriteLine();
    WriteLineIndent("// Check if the struct value is valid");
    WriteLineIndent("func verify(fbeVerifyType: Bool = true) -> Bool {");
    Indent(1);
    WriteLineIndent("if (_buffer.offset + fbeOffset + fbeSize) > _buffer.size {");
    Indent(1);
    WriteLineIndent("return true");
    Indent(-1);
    WriteLineIndent("}");
    WriteLine();
    WriteLineIndent("let fbeStructOffset = Int(readUInt32(offset: fbeOffset))");
    WriteLineIndent("if (fbeStructOffset == 0) || ((_buffer.offset + fbeStructOffset + 4 + 4) > _buffer.size) {");
    Indent(1);
    WriteLineIndent("return false");
    Indent(-1);
    WriteLineIndent("}");
    WriteLine();
    WriteLineIndent("let fbeStructSize = Int(readUInt32(offset: fbeStructOffset))");
    WriteLineIndent("if fbeStructSize < (4 + 4) {");
    Indent(1);
    WriteLineIndent("return false");
    Indent(-1);
    WriteLineIndent("}");
    WriteLine();
    WriteLineIndent("let fbeStructType = Int(readUInt32(offset: fbeStructOffset + 4))");
    WriteLineIndent("if fbeVerifyType && (fbeStructType != fbeType) {");
    Indent(1);
    WriteLineIndent("return false");
    Indent(-1);
    WriteLineIndent("}");
    WriteLine();
    WriteLineIndent("_buffer.shift(offset: fbeStructOffset)");
    WriteLineIndent("let fbeResult = verifyFields(fbeStructSize: fbeStructSize)");
    WriteLineIndent("_buffer.unshift(offset: fbeStructOffset)");
    WriteLineIndent("return fbeResult");
    Indent(-1);
    WriteLineIndent("}");

    // Generate struct field model verifyFields() method
    WriteLine();
    WriteLineIndent("// Check if the struct fields are valid");
    WriteLineIndent("public func verifyFields(fbeStructSize: Int) -> Bool {");
    Indent(1);
    if ((s->base && !s->base->empty()) || (s->body && !s->body->fields.empty()))
    {
        WriteLineIndent("var fbeCurrentSize = 4 + 4");
        if (s->base && !s->base->empty())
        {
            WriteLine();
            WriteLineIndent("if fbeCurrentSize + parent.fbeBody - 4 - 4 > fbeStructSize {");
            Indent(1);
            WriteLineIndent("return true");
            Indent(-1);
            WriteLineIndent("}");
            WriteLineIndent("if !parent.verifyFields(fbeStructSize: fbeStructSize) {");
            Indent(1);
            WriteLineIndent("return false");
            Indent(-1);
            WriteLineIndent("}");
            WriteLineIndent("fbeCurrentSize += parent.fbeBody - 4 - 4");
        }
        if (s->body)
        {
            for (const auto& field : s->body->fields)
            {
                WriteLine();
                WriteLineIndent("if (fbeCurrentSize + " + *field->name + ".fbeSize) > fbeStructSize {");
                Indent(1);
                WriteLineIndent("return true");
                Indent(-1);
                WriteLineIndent("}");
                WriteLineIndent("if !" + *field->name + ".verify() {");
                Indent(1);
                WriteLineIndent("return false");
                Indent(-1);
                WriteLineIndent("}");
                WriteLineIndent("fbeCurrentSize += " + *field->name + ".fbeSize");
            }
        }
        WriteLine();
    }
    WriteLineIndent("return true");
    Indent(-1);
    WriteLineIndent("}");

    // Generate struct field model getBegin() method
    WriteLine();
    WriteLineIndent("// Get the struct value (begin phase)");
    WriteLineIndent("func getBegin() -> Int {");
    Indent(1);
    WriteLineIndent("if _buffer.offset + fbeOffset + fbeSize > _buffer.size {");
    Indent(1);
    WriteLineIndent("return 0");
    Indent(-1);
    WriteLineIndent("}");
    WriteLine();
    WriteLineIndent("let fbeStructOffset = Int(readUInt32(offset: fbeOffset))");
    WriteLineIndent("if (fbeStructOffset == 0) || ((_buffer.offset + fbeStructOffset + 4 + 4) > _buffer.size) {");
    Indent(1);
    WriteLineIndent("assertionFailure(\"Model is broken!\")");
    WriteLineIndent("return 0");
    Indent(-1);
    WriteLineIndent("}");
    WriteLine();
    WriteLineIndent("let fbeStructSize = Int(readUInt32(offset: fbeStructOffset))");
    WriteLineIndent("if fbeStructSize < 4 + 4 {");
    Indent(1);
    WriteLineIndent("assertionFailure(\"Model is broken!\")");
    WriteLineIndent("return 0");
    Indent(-1);
    WriteLineIndent("}");
    WriteLine();
    WriteLineIndent("_buffer.shift(offset: fbeStructOffset)");
    WriteLineIndent("return fbeStructOffset");
    Indent(-1);
    WriteLineIndent("}");

    // Generate struct field model getEnd() method
    WriteLine();
    WriteLineIndent("// Get the struct value (end phase)");
    WriteLineIndent("func getEnd(fbeBegin: Int) {");
    Indent(1);
    WriteLineIndent("_buffer.unshift(offset: fbeBegin)");
    Indent(-1);
    WriteLineIndent("}");

    // Generate struct field model get() methods
    WriteLine();
    WriteLineIndent("// Get the struct value");
    WriteLineIndent("public func get() -> " + struct_name + " {");
    Indent(1);
    WriteLineIndent("var fbeValue = " + struct_name+ "()");
    WriteLineIndent("return get(fbeValue: &fbeValue)");
    Indent(-1);
    WriteLineIndent("}");

    WriteLine();
    WriteLineIndent("public func get(fbeValue: inout " + struct_name + ") -> " + struct_name + " {");
    Indent(1);
    WriteLineIndent("let fbeBegin = getBegin()");
    WriteLineIndent("if fbeBegin == 0 {");
    Indent(1);
    WriteLineIndent("return fbeValue");
    Indent(-1);
    WriteLineIndent("}");
    WriteLine();
    WriteLineIndent("let fbeStructSize = Int(readUInt32(offset: 0))");
    WriteLineIndent("getFields(fbeValue: &fbeValue, fbeStructSize: fbeStructSize)");
    WriteLineIndent("getEnd(fbeBegin: fbeBegin)");
    WriteLineIndent("return fbeValue");
    Indent(-1);
    WriteLineIndent("}");

    // Generate struct field model getFields() method
    WriteLine();
    WriteLineIndent("// Get the struct fields values");
    WriteLineIndent("public func getFields(fbeValue: inout " + struct_name + ", fbeStructSize: Int) {");
    Indent(1);
    if ((s->base && !s->base->empty()) || (s->body && !s->body->fields.empty()))
    {
        WriteLineIndent("var fbeCurrentSize = 4 + 4");
        if (s->base && !s->base->empty())
        {
            WriteLine();
            WriteLineIndent("if fbeCurrentSize + parent.fbeBody - 4 - 4 <= fbeStructSize {");
            Indent(1);
            WriteLineIndent("parent.getFields(fbeValue: &fbeValue.parent, fbeStructSize: fbeStructSize)");
            Indent(-1);
            WriteLineIndent("}");
            WriteLineIndent("fbeCurrentSize += parent.fbeBody - 4 - 4");
        }
        if (s->body)
        {
            for (const auto& field : s->body->fields)
            {
                WriteLine();
                WriteLineIndent("if fbeCurrentSize + " + *field->name + ".fbeSize <= fbeStructSize {");
                Indent(1);
                if (field->array || field->vector || field->list || field->set || field->map || field->hash)
                    WriteLineIndent(*field->name + ".get(values: &fbeValue." + *field->name + ")");
                else
                    WriteLineIndent("fbeValue." + *field->name + " = " + *field->name + ".get(" + (field->value ? "defaults: " + ConvertConstant(domain, package, *field->type, *field->value, field->optional) : "") + ")");
                Indent(-1);
                WriteLineIndent("} else {");
                Indent(1);
                if (field->vector || field->list || field->set || field->map || field->hash)
                    WriteLineIndent("fbeValue." + *field->name + ".removeAll()");
                else
                    WriteLineIndent("fbeValue." + *field->name + " = " + ConvertDefault(domain, package, *field));
                Indent(-1);
                WriteLineIndent("}");
                WriteLineIndent("fbeCurrentSize += " + *field->name + ".fbeSize");
            }
        }
    }
    Indent(-1);
    WriteLineIndent("}");

    // Generate struct field model setBegin() method
    WriteLine();
    WriteLineIndent("// Set the struct value (begin phase)");
    WriteLineIndent("func setBegin() throws -> Int {");
    Indent(1);
    WriteLineIndent("if (_buffer.offset + fbeOffset + fbeSize) > _buffer.size {");
    Indent(1);
    WriteLineIndent("assertionFailure(\"Model is broken!\")");
    WriteLineIndent("return 0");
    Indent(-1);
    WriteLineIndent("}");
    WriteLine();
    WriteLineIndent("let fbeStructSize = fbeBody");
    WriteLineIndent("let fbeStructOffset = try _buffer.allocate(size: fbeStructSize) - _buffer.offset");
    WriteLineIndent("if (fbeStructOffset <= 0) || ((_buffer.offset + fbeStructOffset + fbeStructSize) > _buffer.size) {");
    Indent(1);
    WriteLineIndent("assertionFailure(\"Model is broken!\")");
    WriteLineIndent("return 0");
    Indent(-1);
    WriteLineIndent("}");
    WriteLine();
    WriteLineIndent("write(offset: fbeOffset, value: UInt32(fbeStructOffset))");
    WriteLineIndent("write(offset: fbeStructOffset, value: UInt32(fbeStructSize))");
    WriteLineIndent("write(offset: fbeStructOffset + 4, value: UInt32(fbeType))");
    WriteLine();
    WriteLineIndent("_buffer.shift(offset: fbeStructOffset)");
    WriteLineIndent("return fbeStructOffset");
    Indent(-1);
    WriteLineIndent("}");

    // Generate struct field model setEnd() method
    WriteLine();
    WriteLineIndent("// Set the struct value (end phase)");
    WriteLineIndent("public func setEnd(fbeBegin: Int) {");
    Indent(1);
    WriteLineIndent("_buffer.unshift(offset: fbeBegin)");
    Indent(-1);
    WriteLineIndent("}");

    // Generate struct field model set() method
    WriteLine();
    WriteLineIndent("// Set the struct value");
    WriteLineIndent("public func set(value fbeValue: " + struct_name + ") throws {");
    Indent(1);
    WriteLineIndent("let fbeBegin = try setBegin()");
    WriteLineIndent("if fbeBegin == 0 {");
    Indent(1);
    WriteLineIndent("return");
    Indent(-1);
    WriteLineIndent("}");
    WriteLine();
    WriteLineIndent("try setFields(fbeValue: fbeValue)");
    WriteLineIndent("setEnd(fbeBegin: fbeBegin)");
    Indent(-1);
    WriteLineIndent("}");

    // Generate struct field model setFields() method
    WriteLine();
    WriteLineIndent("// Set the struct fields values");
    WriteLineIndent("public func setFields(fbeValue: " + struct_name + ") throws {");
    Indent(1);
    if ((s->base && !s->base->empty()) || (s->body && !s->body->fields.empty()))
    {
        if (s->base && !s->base->empty())
            WriteLineIndent("try parent.setFields(fbeValue: fbeValue.parent)");
        if (s->body)
            for (const auto& field : s->body->fields)
                WriteLineIndent("try " + *field->name + ".set(value: fbeValue." + *field->name + ")");
    }
    Indent(-1);
    WriteLineIndent("}");

    // Generate struct field model end
    Indent(-1);
    WriteLineIndent("}");

    // Generate footer
    GenerateFooter();

    // Store the file
    WriteEnd();
    Store(file);
}

void GeneratorSwift::GenerateStructModel(const std::shared_ptr<Package>& p, const std::shared_ptr<StructType>& s)
{
    std::string domain = ConvertDomain(p);
    std::string package = ConvertPackage(p);
    std::string struct_name = *s->name;

    CppCommon::Path path = (CppCommon::Path(_output) / CreatePackagePath(domain, package)) / "Fbe";

    // Create package path
    CppCommon::Directory::CreateTree(path);

    // Generate the file
    CppCommon::Path file = path / (*s->name + "Model.swift");
    WriteBegin();

    // Generate headers
    GenerateHeader(CppCommon::Path(_input).filename().string());
    GenerateImports(domain, "Fbe");

    // Generate struct model begin
    WriteLine();
    WriteLineIndent("// Fast Binary Encoding " + *s->name + " model");
    WriteLineIndent("public class " + *s->name + "Model: Model {");
    Indent(1);

    // Generate struct model accessor
    WriteLineIndent("public let model: FieldModel" + *s->name);

    // Generate struct model init(buffer: Buffer) method
    WriteLine();
    WriteLineIndent("public override init(buffer: Buffer = Buffer()) {");
    Indent(1);
    WriteLineIndent("model = FieldModel" + *s->name + "(buffer: buffer, offset: 4)");
    WriteLineIndent("super.init(buffer: buffer)");
    Indent(-1);
    WriteLineIndent("}");

    // Generate struct model FBE properties
    WriteLine();
    WriteLineIndent("// Model size");
    WriteLineIndent("public func fbeSize() -> Int { model.fbeSize + model.fbeExtra }");
    WriteLineIndent("// Model type");
    WriteLineIndent("public var fbeType: Int = fbeTypeConst");
    WriteLineIndent("static let fbeTypeConst: Int = FieldModel" + *s->name + ".fbeTypeConst");

    // Generate struct model verify() method
    WriteLine();
    WriteLineIndent("// Check if the struct value is valid");
    WriteLineIndent("public func verify() -> Bool {");
    Indent(1);
    WriteLineIndent("if buffer.offset + model.fbeOffset - 4 > buffer.size {");
    Indent(1);
    WriteLineIndent("return false");
    Indent(-1);
    WriteLineIndent("}");
    WriteLine();
    WriteLineIndent("let fbeFullSize = Int(readUInt32(offset: model.fbeOffset - 4))");
    WriteLineIndent("if fbeFullSize < model.fbeSize {");
    Indent(1);
    WriteLineIndent("return false");
    Indent(-1);
    WriteLineIndent("}");
    WriteLine();
    WriteLineIndent("return model.verify()");
    Indent(-1);
    WriteLineIndent("}");

    // Create a new model (begin phase)
    WriteLine();
    WriteLineIndent("// Create a new model (begin phase)");
    WriteLineIndent("public func createBegin() throws -> Int {");
    Indent(1);
    WriteLineIndent("return try buffer.allocate(size: 4 + model.fbeSize)");
    Indent(-1);
    WriteLineIndent("}");

    // Generate struct model createEnd() method
    WriteLine();
    WriteLineIndent("// Create a new model (end phase)");
    WriteLineIndent("public func createEnd(fbeBegin: Int) -> Int {");
    Indent(1);
    WriteLineIndent("let fbeEnd = buffer.size");
    WriteLineIndent("let fbeFullSize = fbeEnd - fbeBegin");
    WriteLineIndent("write(offset: model.fbeOffset - 4, value: UInt32(fbeFullSize))");
    WriteLineIndent("return fbeFullSize");
    Indent(-1);
    WriteLineIndent("}");

    // Generate struct model serialize() method
    WriteLine();
    WriteLineIndent("// Serialize the struct value");
    WriteLineIndent("public func serialize(value: " + struct_name + ") throws -> Int {");
    Indent(1);
    WriteLineIndent("let fbeBegin = try createBegin()");
    WriteLineIndent("try model.set(value: value)");
    WriteLineIndent("return createEnd(fbeBegin: fbeBegin)");
    Indent(-1);
    WriteLineIndent("}");

    // Generate struct model deserialize() methods
    WriteLine();
    WriteLineIndent("// Deserialize the struct value");
    WriteLineIndent("public func deserialize() -> " + struct_name + " { var value = " + struct_name + "(); _ = deserialize(value: &value); return value }");
    WriteLineIndent("public func deserialize(value: inout " + struct_name + ") -> Int {");
    Indent(1);
    WriteLineIndent("if buffer.offset + model.fbeOffset - 4 > buffer.size {");
    Indent(1);
    WriteLineIndent("value = " + struct_name + "()");
    WriteLineIndent("return 0");
    Indent(-1);
    WriteLineIndent("}");
    WriteLine();
    WriteLineIndent("let fbeFullSize = Int(readUInt32(offset: model.fbeOffset - 4))");
    WriteLineIndent("if fbeFullSize < model.fbeSize {");
    Indent(1);
    WriteLineIndent("assertionFailure(\"Model is broken!\")");
    WriteLineIndent("value = " + struct_name + "()");
    WriteLineIndent("return 0");
    Indent(-1);
    WriteLineIndent("}");
    WriteLine();
    WriteLineIndent("value = model.get(fbeValue: &value)");
    WriteLineIndent("return fbeFullSize");
    Indent(-1);
    WriteLineIndent("}");

    // Generate struct model next() method
    WriteLine();
    WriteLineIndent("// Move to the next struct value");
    WriteLineIndent("public func next(prev: Int) {");
    Indent(1);
    WriteLineIndent("model.fbeShift(size: prev)");
    Indent(-1);
    WriteLineIndent("}");

    // Generate struct model end
    Indent(-1);
    WriteLineIndent("}");

    // Generate footer
    GenerateFooter();

    // Store the file
    WriteEnd();
    Store(file);
}

void GeneratorSwift::GenerateStructFinalModel(const std::shared_ptr<Package>& p, const std::shared_ptr<StructType>& s)
{
    std::string domain = ConvertDomain(p);
    std::string package = ConvertPackage(p);
    std::string struct_name = *s->name;

    CppCommon::Path path = (CppCommon::Path(_output) / CreatePackagePath(domain, package)) / "Fbe";

    // Create package path
    CppCommon::Directory::CreateTree(path);

    // Generate the file
    CppCommon::Path file = path / ("FinalModel" + *s->name + ".swift");
    WriteBegin();

    // Generate headers
    GenerateHeader(CppCommon::Path(_input).filename().string());
    GenerateImports(domain, "Fbe");
    GenerateImports(p);

    // Generate struct final model begin
    WriteLine();
    WriteLineIndent("// Fast Binary Encoding " + *s->name + " final model");
    WriteLineIndent("public class FinalModel" + *s->name + ": FinalModel {");
    Indent(1);

    WriteLineIndent("public var _buffer: Buffer");
    WriteLineIndent("public var _offset: Int");

    WriteLine();

    // Generate struct final model accessors
    if (s->base && !s->base->empty())
        WriteLineIndent("let parent: " + ConvertBaseFieldName(domain, ConvertPackageName(*s->base), true));
    if (s->body)
        for (const auto& field : s->body->fields)
            WriteLineIndent("let " + *field->name + ": " + ConvertTypeFieldDeclaration(domain, *field, true));

    WriteLine();
    WriteLineIndent("// Field type");
    WriteLineIndent("public var fbeType: Int = fbeTypeConst");
    WriteLine();
    if (s->base && !s->base->empty() && (s->type == 0))
        WriteLineIndent("public static let fbeTypeConst: Int = " + ConvertBaseFieldName(domain, ConvertPackageName(*s->base), true) + ".fbeTypeConst");
    else
        WriteLineIndent("public static let fbeTypeConst: Int = " + std::to_string(s->type));

    // Generate struct final model init methods
    WriteLine();
    WriteLineIndent("public required init(buffer: Buffer, offset: Int) {");
    Indent(1);
    WriteLineIndent("_buffer = buffer");
    WriteLineIndent("_offset = offset");
    WriteLine();
    if (s->base && !s->base->empty())
          WriteLineIndent("parent = " + ConvertBaseFieldName(domain, ConvertPackageName(*s->base), true) + "(buffer: buffer, offset: 0)");
    if (s->body)
      for (const auto& field : s->body->fields)
          WriteLineIndent(*field->name + " = " + ConvertTypeFieldInitialization(domain, *field, "0", true));
    Indent(-1);
    WriteLineIndent("}");

    // Generate struct final model FBE properties
    WriteLine();
    WriteLineIndent("// Get the allocation size");
    WriteLineIndent("public func fbeAllocationSize(value fbeValue: " + struct_name + ") -> Int {");
    Indent(1);
    WriteLineIndent("return 0");
    Indent(1);
    if (s->base && !s->base->empty())
        WriteLineIndent("+ parent.fbeAllocationSize(value: fbeValue.parent)");
    if (s->body)
        for (const auto& field : s->body->fields)
            WriteLineIndent("+ " + *field->name + ".fbeAllocationSize(value: fbeValue." + *field->name + ")");
    Indent(-1);
    Indent(-1);
    WriteLineIndent("}");

    // Generate struct final model verify() methods
    WriteLine();
    WriteLineIndent("// Check if the struct value is valid");
    WriteLineIndent("public func verify() -> Int {");
    Indent(1);
    WriteLineIndent("_buffer.shift(offset: fbeOffset)");
    WriteLineIndent("let fbeResult = verifyFields()");
    WriteLineIndent("_buffer.unshift(offset: fbeOffset)");
    WriteLineIndent("return fbeResult");
    Indent(-1);
    WriteLineIndent("}");

    // Generate struct final model verifyFields() method
    WriteLine();
    WriteLineIndent("// Check if the struct fields are valid");
    WriteLineIndent("public func verifyFields() -> Int {");
    Indent(1);
    if ((s->base && !s->base->empty()) || (s->body && !s->body->fields.empty()))
    {
        WriteLineIndent("var fbeCurrentOffset: Int = 0");
        WriteLineIndent("var fbeFieldSize: Int = 0");
        if (s->base && !s->base->empty())
        {
            WriteLine();
            WriteLineIndent("parent.fbeOffset = fbeCurrentOffset");
            WriteLineIndent("fbeFieldSize = parent.verifyFields()");
            WriteLineIndent("if fbeFieldSize == Int.max {");
            Indent(1);
            WriteLineIndent("return Int.max");
            Indent(-1);
            WriteLineIndent("}");
            WriteLineIndent("fbeCurrentOffset += fbeFieldSize");
        }
        if (s->body)
        {
            for (const auto& field : s->body->fields)
            {
                WriteLine();
                WriteLineIndent(*field->name + ".fbeOffset = fbeCurrentOffset");
                WriteLineIndent("fbeFieldSize = " + *field->name + ".verify()");
                WriteLineIndent("if fbeFieldSize == Int.max {");
                Indent(1);
                WriteLineIndent("return Int.max");
                Indent(-1);
                WriteLineIndent("}");
                WriteLineIndent("fbeCurrentOffset += fbeFieldSize");
            }
        }
        WriteLine();
        WriteLineIndent("return fbeCurrentOffset");
    }
    else
        WriteLineIndent("return 0");
    Indent(-1);
    WriteLineIndent("}");

    // Generate struct final model get() methods
    WriteLine();
    WriteLineIndent("// Get the struct value");
    WriteLineIndent("public func get(size: inout Size) -> " + struct_name + " {");
    Indent(1);
    WriteLineIndent("var fbeValue = " + struct_name + "()");
    WriteLineIndent("return get(size: &size, fbeValue: &fbeValue)");
    Indent(-1);
    WriteLineIndent("}");

    WriteLine();
    WriteLineIndent("// Get the struct value");
    WriteLineIndent("public func get(size: inout Size, fbeValue: inout " + struct_name + ") -> " + struct_name + " {");
    Indent(1);
    WriteLineIndent("_buffer.shift(offset: fbeOffset)");
    WriteLineIndent("size.value = getFields(fbeValue: &fbeValue)");
    WriteLineIndent("_buffer.unshift(offset: fbeOffset)");
    WriteLineIndent("return fbeValue");
    Indent(-1);
    WriteLineIndent("}");

    // Generate struct final model getFields() method
    WriteLine();
    WriteLineIndent("// Get the struct fields values");
    WriteLineIndent("public func getFields(fbeValue: inout " + struct_name + ") -> Int {");
    Indent(1);
    if ((s->base && !s->base->empty()) || (s->body && !s->body->fields.empty()))
    {
        WriteLineIndent("var fbeCurrentOffset: Int = 0");
        WriteLineIndent("var fbeCurrentSize: Int = 0");
        WriteLineIndent("var fbeFieldSize = Size()");

        if (s->base && !s->base->empty())
        {
            WriteLine();
            WriteLineIndent("parent.fbeOffset = fbeCurrentOffset");
            WriteLineIndent("fbeFieldSize.value = parent.getFields(fbeValue: &fbeValue.parent)");
            WriteLineIndent("fbeCurrentOffset += fbeFieldSize.value");
            WriteLineIndent("fbeCurrentSize += fbeFieldSize.value");
        }
        if (s->body)
        {
            for (const auto& field : s->body->fields)
            {
                WriteLine();
                WriteLineIndent(*field->name + ".fbeOffset = fbeCurrentOffset");
                if (field->array || field->vector || field->list || field->set || field->map || field->hash)
                    WriteLineIndent("fbeFieldSize.value = " + *field->name + ".get(values: &fbeValue." + *field->name + ")");
                else
                    WriteLineIndent("fbeValue." + *field->name + " = " + *field->name + ".get(size: &fbeFieldSize)");
                WriteLineIndent("fbeCurrentOffset += fbeFieldSize.value");
                WriteLineIndent("fbeCurrentSize += fbeFieldSize.value");
            }
        }
        WriteLine();
        WriteLineIndent("return fbeCurrentSize");
    }
    else
        WriteLineIndent("return 0");
    Indent(-1);
    WriteLineIndent("}");

    // Generate struct final model set() method
    WriteLine();
    WriteLineIndent("// Set the struct value");
    WriteLineIndent("public func set(value fbeValue: " + struct_name + ") throws -> Int {");
    Indent(1);
    WriteLineIndent("_buffer.shift(offset: fbeOffset)");
    WriteLineIndent("let fbeSize = try setFields(fbeValue: fbeValue)");
    WriteLineIndent("_buffer.unshift(offset: fbeOffset)");
    WriteLineIndent("return fbeSize");
    Indent(-1);
    WriteLineIndent("}");

    // Generate struct final model setFields() method
    WriteLine();
    WriteLineIndent("// Set the struct fields values");
    WriteLineIndent("public func setFields(fbeValue: " + struct_name + ") throws -> Int {");
    Indent(1);
    if ((s->base && !s->base->empty()) || (s->body && !s->body->fields.empty()))
    {
        WriteLineIndent("var fbeCurrentOffset: Int = 0");
        WriteLineIndent("var fbeCurrentSize: Int = 0");
        WriteLineIndent("let fbeFieldSize = Size()");
        if (s->base && !s->base->empty())
        {
            WriteLine();
            WriteLineIndent("parent.fbeOffset = fbeCurrentOffset");
            WriteLineIndent("fbeFieldSize.value = try parent.setFields(fbeValue: fbeValue.parent)");
            WriteLineIndent("fbeCurrentOffset += fbeFieldSize.value");
            WriteLineIndent("fbeCurrentSize += fbeFieldSize.value");
        }
        if (s->body)
        {
            for (const auto& field : s->body->fields)
            {
                WriteLine();
                WriteLineIndent(*field->name + ".fbeOffset = fbeCurrentOffset");
                WriteLineIndent("fbeFieldSize.value = try " + *field->name + ".set(value: fbeValue." + *field->name + ")");
                WriteLineIndent("fbeCurrentOffset += fbeFieldSize.value");
                WriteLineIndent("fbeCurrentSize += fbeFieldSize.value");
            }
        }
        WriteLine();
        WriteLineIndent("return fbeCurrentSize");
    }
    else
        WriteLineIndent("return 0");
    Indent(-1);
    WriteLineIndent("}");

    // Generate struct final model end
    Indent(-1);
    WriteLineIndent("}");

    // Generate footer
    GenerateFooter();

    // Store the file
    WriteEnd();
    Store(file);
}

void GeneratorSwift::GenerateStructModelFinal(const std::shared_ptr<Package>& p, const std::shared_ptr<StructType>& s)
{
    std::string domain = ConvertDomain(p);
    std::string package = ConvertPackage(p);
    std::string struct_name = *s->name;

    CppCommon::Path path = (CppCommon::Path(_output) / CreatePackagePath(domain, package)) / "Fbe";

    // Create package path
    CppCommon::Directory::CreateTree(path);

    // Generate the file
    CppCommon::Path file = path / (*s->name + "FinalModel.swift");
    WriteBegin();

    // Generate headers
    GenerateHeader(CppCommon::Path(_input).filename().string());
    GenerateImports(domain, "Fbe");

    // Generate struct model final begin
    WriteLine();
    WriteLineIndent("// Fast Binary Encoding " + *s->name + " final model");
    WriteLineIndent("public class " + *s->name + "FinalModel: Model {");
    Indent(1);

    // Generate struct model final accessor
    WriteLineIndent("private let _model: FinalModel" + *s->name);

    // Generate struct final model init methods
    WriteLine();
    WriteLineIndent("public override init(buffer: Buffer = Buffer()) {");
    Indent(1);
    WriteLineIndent("_model = FinalModel" + *s->name + "(buffer: buffer, offset: 8)");
    WriteLine();
    WriteLineIndent("super.init(buffer: buffer)");
    Indent(-1);
    WriteLineIndent("}");

    // Generate struct model final FBE properties
    WriteLine();
    WriteLineIndent("// Model type");
    WriteLineIndent("public var fbeType: Int = fbeTypeConst");
    WriteLine();
    WriteLineIndent("static let fbeTypeConst: Int = FinalModel" + *s->name + ".fbeTypeConst");

    // Generate struct model final verify() method
    WriteLine();
    WriteLineIndent("// Check if the struct value is valid");
    WriteLineIndent("public func verify() -> Bool {");
    Indent(1);
    WriteLineIndent("if (buffer.offset + _model.fbeOffset) > buffer.size {");
    Indent(1);
    WriteLineIndent("return false");
    Indent(-1);
    WriteLineIndent("}");
    WriteLine();
    WriteLineIndent("let fbeStructSize = Int(readUInt32(offset: _model.fbeOffset - 8))");
    WriteLineIndent("let fbeStructType = Int(readUInt32(offset: _model.fbeOffset - 4))");
    WriteLineIndent("if (fbeStructSize <= 0) || (fbeStructType != fbeType) {");
    Indent(1);
    WriteLineIndent("return false");
    Indent(-1);
    WriteLineIndent("}");
    WriteLine();
    WriteLineIndent("return ((8 + _model.verify()) == fbeStructSize)");
    Indent(-1);
    WriteLineIndent("}");

    // Generate struct model final serialize() method
    WriteLine();
    WriteLineIndent("// Serialize the struct value");
    WriteLineIndent("public func serialize(value: " + struct_name + ") throws -> Int {");
    Indent(1);
    WriteLineIndent("let fbeInitialSize = buffer.size");
    WriteLine();
    WriteLineIndent("let fbeStructType = fbeType");
    WriteLineIndent("var fbeStructSize = 8 + _model.fbeAllocationSize(value: value)");
    WriteLineIndent("let fbeStructOffset = try buffer.allocate(size: fbeStructSize) - buffer.offset");
    WriteLineIndent("if (buffer.offset + fbeStructOffset + fbeStructSize) > buffer.size {");
    Indent(1);
    WriteLineIndent("assertionFailure(\"Model is broken!\")");
    WriteLineIndent("return 0");
    Indent(-1);
    WriteLineIndent("}");
    WriteLine();
    WriteLineIndent("fbeStructSize = try _model.set(value: value) + 8");
    WriteLineIndent("try buffer.resize(size: fbeInitialSize + fbeStructSize)");
    WriteLine();
    WriteLineIndent("write(offset: _model.fbeOffset - 8, value: UInt32(fbeStructSize))");
    WriteLineIndent("write(offset: _model.fbeOffset - 4, value: UInt32(fbeStructType))");
    WriteLine();
    WriteLineIndent("return fbeStructSize");
    Indent(-1);
    WriteLineIndent("}");

    // Generate struct model final deserialize() methods
    WriteLine();
    WriteLineIndent("// Deserialize the struct value");
    WriteLineIndent("public func deserialize() -> " + struct_name + " { var value = " + struct_name + "(); _ = deserialize(value: &value); return value }");
    WriteLine();
    WriteLineIndent("public func deserialize(value: inout " + struct_name + ") -> Int {");
    Indent(1);
    WriteLineIndent("if (buffer.offset + _model.fbeOffset) > buffer.size {");
    Indent(1);
    WriteLineIndent("assertionFailure(\"Model is broken!\")");
    WriteLineIndent("return 0");
    Indent(-1);
    WriteLineIndent("}");
    WriteLine();
    WriteLineIndent("let fbeStructSize = Int32(readUInt32(offset: _model.fbeOffset - 8))");
    WriteLineIndent("let fbeStructType = Int32(readUInt32(offset: _model.fbeOffset - 4))");
    WriteLineIndent("if (fbeStructSize <= 0) || (fbeStructType != fbeType) {");
    Indent(1);
    WriteLineIndent("assertionFailure(\"Model is broken!\")");
    WriteLineIndent("return 8");
    Indent(-1);
    WriteLineIndent("}");
    WriteLine();
    WriteLineIndent("var fbeSize = Size()");
    WriteLineIndent("value = _model.get(size: &fbeSize, fbeValue: &value)");
    WriteLineIndent("return 8 + fbeSize.value");
    Indent(-1);
    WriteLineIndent("}");

    // Generate struct model final next() method
    WriteLine();
    WriteLineIndent("// Move to the next struct value");
    WriteLineIndent("public func next(prev: Int) {");
    Indent(1);
    WriteLineIndent("_model.fbeShift(size: prev)");
    Indent(-1);
    WriteLineIndent("}");

    // Generate struct model final end
    Indent(-1);
    WriteLineIndent("}");

    // Generate footer
    GenerateFooter();

    // Store the file
    WriteEnd();
    Store(file);
}

void GeneratorSwift::GenerateProtocolVersion(const std::shared_ptr<Package>& p)
{
    std::string domain = ConvertDomain(p);
    std::string package = ConvertPackage(p);

    CppCommon::Path path = (CppCommon::Path(_output) / CreatePackagePath(domain, package)) / "Fbe";

    // Create package path
    CppCommon::Directory::CreateTree(path);

    // Generate the file
    CppCommon::Path file = path / ("ProtocolVersion.swift");
    WriteBegin();

    // Generate headers
    GenerateHeader(CppCommon::Path(_input).filename().string());

    // Generate protocol version class
    WriteLineIndent("// Fast Binary Encoding " + domain + package + " protocol version");
    WriteLineIndent("public struct ProtocolVersion {");
    Indent(1);
    WriteLineIndent("// Protocol major version");
    WriteLineIndent("public static let Major = " + std::to_string(p->version->major));
    WriteLineIndent("// Protocol minor version");
    WriteLineIndent("public static let Minor = " + std::to_string(p->version->minor));
    Indent(-1);
    WriteLineIndent("}");

    // Generate footer
    GenerateFooter();

    // Store the file
    WriteEnd();
    Store(file);
}

void GeneratorSwift::GenerateSender(const std::shared_ptr<Package>& p, bool final)
{
    std::string domain = ConvertDomain(p);
    std::string package = ConvertPackage(p);

    CppCommon::Path path = (CppCommon::Path(_output) / CreatePackagePath(domain, package)) / "Fbe";

    // Create package path
    CppCommon::Directory::CreateTree(path);

    std::string sender = (final ? "FinalSender" : "Sender");
    std::string model = (final ? "FinalModel" : "Model");

    // Generate the file
    CppCommon::Path file = path / (sender + ".swift");
    WriteBegin();

    // Generate headers
    GenerateHeader(CppCommon::Path(_input).filename().string());
    GenerateImports(domain, "Fbe");
    GenerateImports("", "Foundation");
    GenerateImports(p);

    // Generate sender begin
    WriteLine();
    if (final)
        WriteLineIndent("// Fast Binary Encoding " + domain + package + " final sender");
    else
        WriteLineIndent("// Fast Binary Encoding " + domain + package + " sender");
    WriteLineIndent("open class " + sender + ": " + domain + "Fbe.SenderProtocol {");
    Indent(1);

    // Generate imported senders accessors
    if (p->import)
    {
        WriteLineIndent("// Imported senders");
        for (const auto& import : p->import->imports)
            WriteLineIndent("let " + ConvertPackageName(*import) + "Sender: " + domain + ConvertPackageName(*import) + "." + sender);
        WriteLine();
    }

    // Generate sender models accessors
    if (p->body)
    {
        WriteLineIndent("// Sender models accessors");
        for (const auto& s : p->body->structs)
            if (s->message)
                WriteLineIndent("private let " + *s->name + "Model: " + *s->name + model);
        WriteLine();
    }

    WriteLineIndent("public var buffer: Buffer = Buffer()");
    WriteLineIndent("public var final: Bool = false");
    WriteLine();

    // Generate sender constructors
    WriteLineIndent("public init() {");
    Indent(1);
    if (p->import)
        for (const auto& import : p->import->imports)
            WriteLineIndent(ConvertPackageName(*import) + "Sender = " + domain + ConvertPackageName(*import) + "." + sender + "(buffer: buffer)");
    if (p->body)
    {
        for (const auto& s : p->body->structs)
        {
            if (s->message)
            {
                std::string struct_name = domain + package + "." + *s->name;
                WriteLineIndent(*s->name + "Model = " + struct_name + model + "(buffer: buffer)");
            }

        }
    }
    WriteLineIndent("build(with: " + std::string(final ? "true" : "false") + ")");
    Indent(-1);
    WriteLineIndent("}");
    WriteLine();
    WriteLineIndent("public init(buffer: " + domain + "Fbe.Buffer) {");
    Indent(1);
    if (p->import)
        for (const auto& import : p->import->imports)
            WriteLineIndent(ConvertPackageName(*import) + "Sender = " + domain + ConvertPackageName(*import) + "." + sender + "(buffer: buffer)");
    if (p->body)
    {
        for (const auto& s : p->body->structs)
        {
            if (s->message)
            {
                std::string struct_name = domain + package + "." + *s->name;
                WriteLineIndent(*s->name + "Model = " + struct_name + model + "(buffer: buffer)");
            }

        }

    }
    WriteLineIndent("build(with: buffer, final: " + std::string(final ? "true" : "false") + ")");
    Indent(-1);
    WriteLineIndent("}");
    WriteLine();

    bool messages = false;
    for (const auto& s : p->body->structs)
        if (s->message)
            messages = true;

    WriteLineIndent("public func send(obj: Any) throws -> Int {");
    Indent(1);
    WriteLineIndent("guard let listener = self as? " + domain + "Fbe.SenderListener else { return 0 }");
    WriteLineIndent("return try send(obj: obj, listener: listener)");
    Indent(-1);
    WriteLineIndent("}");
    WriteLine();

    // Generate generic sender method
    WriteLineIndent("public func send(obj: Any, listener: " + domain + "Fbe.SenderListener) throws -> Int {");
    Indent(1);
    if (p->body && messages)
    {
        WriteLineIndent("let objType = type(of: obj)");
        for (const auto& s : p->body->structs)
        {
            if (s->message)
            {
                std::string struct_name = domain + package + "." + *s->name;
                WriteLineIndent("if objType == " + struct_name + ".self, let value = obj as? " + struct_name + " { return try send(value: value, listener: listener) }");
            }
        }
    }
    WriteLine();
    if (p->import)
    {
        WriteLineIndent("// Try to send using imported senders");
        WriteLineIndent("var result: Int = 0");
        for (const auto& import : p->import->imports)
        {
            WriteLineIndent("result = try " + ConvertPackageName(*import) + "Sender.send(obj: obj, listener: listener)");
            WriteLineIndent("if result > 0 {");
            Indent(1);
            WriteLineIndent("return result");
            WriteLineIndent("}");
            Indent(-1);
        }
        WriteLine();
    }
    WriteLineIndent("return 0");
    Indent(-1);
    WriteLineIndent("}");

    // Generate sender methods
    if (p->body)
    {
        for (const auto& s : p->body->structs)
        {
            if (s->message)
            {
                std::string struct_name = domain + package + "." + *s->name;
                WriteLine();
                WriteLineIndent("public func send(value: " + struct_name + ") throws -> Int {");
                Indent(1);
                WriteLineIndent("guard let listener = self as? " + domain + "Fbe.SenderListener else { return 0 }");
                WriteLineIndent("return try send(value: value, listener: listener)");
                Indent(-1);
                WriteLineIndent("}");

                WriteLine();
                WriteLineIndent("public func send(value: " + struct_name + ", listener: " + domain + "Fbe.SenderListener) throws -> Int {");
                Indent(1);
                WriteLineIndent("// Serialize the value into the FBE stream");
                WriteLineIndent("let serialized = try " + *s->name + "Model.serialize(value: value)");
                WriteLineIndent("assert(serialized > 0, \"" + struct_name + " serialization failed!\")");
                WriteLineIndent("assert(" + *s->name + "Model.verify(), \"" + struct_name + " validation failed!\")");
                WriteLine();
                WriteLineIndent("// Log the value");
                WriteLineIndent("if listener.logging {");
                Indent(1);
                WriteLineIndent("let message = value.description");
                WriteLineIndent("listener.onSendLog(message: message)");
                Indent(-1);
                WriteLineIndent("}");
                WriteLine();
                WriteLineIndent("// Send the serialized value");
                WriteLineIndent("return try sendSerialized(listener: listener, serialized: serialized)");
                Indent(-1);
                WriteLineIndent("}");
            }
        }
    }
    // Generate sender end
    Indent(-1);
    WriteLineIndent("}");

    // Generate footer
    GenerateFooter();

    // Store the file
    WriteEnd();
    Store(file);
}

void GeneratorSwift::GenerateReceiver(const std::shared_ptr<Package>& p, bool final)
{
    std::string domain = ConvertDomain(p);
    std::string package = ConvertPackage(p);

    CppCommon::Path path = (CppCommon::Path(_output) / CreatePackagePath(domain, package)) / "Fbe";

    // Create package path
    CppCommon::Directory::CreateTree(path);

    std::string listener = (final ? "FinalReceiverListener" : "ReceiverListener");
    std::string receiver = (final ? "FinalReceiver" : "Receiver");
    std::string model = (final ? "FinalModel" : "Model");

    // Generate the file
    CppCommon::Path file = path / (receiver + ".swift");
    WriteBegin();

    // Generate headers
    GenerateHeader(CppCommon::Path(_input).filename().string());
    GenerateImports("", "Foundation");
    GenerateImports(domain, "Fbe");
    GenerateImports(p);

    // Generate receiver begin
    WriteLine();
    if (final)
        WriteLineIndent("// Fast Binary Encoding " + domain + package + " final receiver");
    else
        WriteLineIndent("// Fast Binary Encoding " + domain + package + " receiver");
    WriteLineIndent("open class " + receiver + ": " + domain +  "Fbe.ReceiverProtocol {");
    Indent(1);

    // Generate imported receivers accessors
    if (p->import)
    {
        WriteLineIndent("// Imported receivers");
        for (const auto& import : p->import->imports)
            WriteLineIndent("let " + ConvertPackageName(*import) + "Receiver: " + domain + ConvertPackageName(*import) + "." + receiver + "?");
        WriteLine();
    }

    // Generate receiver models accessors
    if (p->body)
    {
        WriteLineIndent("// Receiver values accessors");
        for (const auto& s : p->body->structs)
        {
            if (s->message)
            {
                std::string struct_name = domain + package + "." + *s->name;
                WriteLineIndent("private var " + *s->name + "Value: " + struct_name);
            }
        }
        WriteLine();
        WriteLineIndent("// Receiver models accessors");
        for (const auto& s : p->body->structs)
        {
            if (s->message)
            {
                std::string struct_name = domain + package + "." + *s->name;
                WriteLineIndent("private var " + *s->name + "Model: " + *s->name + model);
            }
        }

        WriteLine();
    }


    WriteLineIndent("public var buffer: Buffer = Buffer()");
    WriteLineIndent("public var final: Bool = false");
    WriteLine();

    // Generate receiver constructors
    WriteLineIndent("public init() {");
    Indent(1);
    if (p->import)
        for (const auto& import : p->import->imports)
            WriteLineIndent(ConvertPackageName(*import) + "Receiver = " + domain + ConvertPackageName(*import) + "." + receiver + "(buffer: buffer)");
    if (p->body)
    {
        for (const auto& s : p->body->structs)
        {
            if (s->message)
            {
                std::string struct_name = domain + package + "." + *s->name;
                WriteLineIndent(*s->name + "Value = " + struct_name + "()");
                WriteLineIndent(*s->name + "Model = " + struct_name + model + "()");
            }
        }
    }
    WriteLineIndent("build(final: " + std::string(final ? "true" : "false") + ")");
    Indent(-1);
    WriteLineIndent("}");
    WriteLine();

    WriteLineIndent("public init(buffer: " + domain + "Fbe.Buffer) {");
    Indent(1);
    if (p->import)
        for (const auto& import : p->import->imports)
            WriteLineIndent(ConvertPackageName(*import) + "Receiver = " + domain + ConvertPackageName(*import) + "." + receiver + "(buffer: buffer)");
    if (p->body)
    {
        for (const auto& s : p->body->structs)
        {
            if (s->message)
            {
                std::string struct_name = domain + package + "." + *s->name;
                WriteLineIndent(*s->name + "Value = " + struct_name + "()");
                WriteLineIndent(*s->name + "Model = " + struct_name + model + "()");
            }
        }
    }
    WriteLineIndent("build(with: buffer, final: " + std::string(final ? "true" : "false") + ")");
    Indent(-1);
    WriteLineIndent("}");
    WriteLine();

    bool messages = false;
    for (const auto& s : p->body->structs)
        if (s->message)
            messages = true;

    // Generate receiver message handler
    WriteLineIndent("open func onReceive(type: Int, buffer: Data, offset: Int, size: Int) -> Bool {");
    Indent(1);
    WriteLineIndent("guard let listener = self as? " + listener + " else { return false }");
    WriteLineIndent("return onReceiveListener(listener: listener, type: type, buffer: buffer, offset: offset, size: size)");
    Indent(-1);
    WriteLineIndent("}");
    WriteLine();
    WriteLineIndent("open func onReceiveListener(listener: " + listener + ", type: Int, buffer: Data, offset: Int, size: Int) -> Bool {");
    Indent(1);
    if (p->body && messages)
    {
        WriteLineIndent("switch type {");
        for (const auto& s : p->body->structs)
        {
            if (s->message)
            {
                WriteLineIndent("case " + domain + package + "." + *s->name + model + ".fbeTypeConst:");
                Indent(1);
                WriteLineIndent("// Deserialize the value from the FBE stream");
                WriteLineIndent(*s->name + "Model.attach(buffer: buffer, offset: offset)");
                WriteLineIndent("assert(" + *s->name + "Model.verify(), \"" + package + "." + *s->name + " validation failed!\")");
                WriteLineIndent("let deserialized = " + *s->name + "Model.deserialize(value: &" + *s->name + "Value)");
                WriteLineIndent("assert(deserialized > 0, \"" + package + "." + *s->name + " deserialization failed!\")");
                WriteLine();
                WriteLineIndent("// Log the value");
                WriteLineIndent("if listener.logging {");
                Indent(1);
                WriteLineIndent("let message = " + *s->name + "Value.description");
                WriteLineIndent("listener.onReceiveLog(message: message)");
                Indent(-1);
                WriteLineIndent("}");
                WriteLine();
                WriteLineIndent("// Call receive handler with deserialized value");
                WriteLineIndent("listener.onReceive(value: " + *s->name + "Value)");
                WriteLineIndent("return true");
                Indent(-1);
            }
        }
        WriteLineIndent("default: break");
        WriteLineIndent("}");
    }
    if (p->import)
    {
        WriteLine();
        for (const auto& import : p->import->imports)
        {
            WriteLineIndent("if let " + ConvertPackageName(*import) + "Receiver = " + ConvertPackageName(*import) + "Receiver, " + ConvertPackageName(*import) + "Receiver.onReceiveListener(listener: listener, type: type, buffer: buffer, offset: offset, size: size) {");
            Indent(1);
            WriteLineIndent("return true");
            Indent(-1);
            WriteLineIndent("}");
        }
    }
    WriteLine();
    WriteLineIndent("return false");
    Indent(-1);
    WriteLineIndent("}");

    // Generate receiver end
    Indent(-1);
    WriteLineIndent("}");

    // Generate footer
    GenerateFooter();

    // Store the file
    WriteEnd();
    Store(file);
}

void GeneratorSwift::GenerateReceiverListener(const std::shared_ptr<Package>& p, bool final)
{
    std::string domain = ConvertDomain(p);
    std::string package = ConvertPackage(p);

    CppCommon::Path path = (CppCommon::Path(_output) / CreatePackagePath(domain, package)) / "Fbe";

    // Create package path
    CppCommon::Directory::CreateTree(path);

    std::string listener = (final ? "FinalReceiverListener" : "ReceiverListener");

    // Generate the file
    CppCommon::Path file = path / (listener + ".swift");
    WriteBegin();

    // Generate headers
    GenerateHeader(CppCommon::Path(_input).filename().string());
    GenerateImports(domain, "Fbe");
    GenerateImports(p);

    // Generate receiver listener begin
    WriteLine();
    if (final)
        WriteLineIndent("// Fast Binary Encoding " + package + " final receiver listener");
    else
        WriteLineIndent("// Fast Binary Encoding " + package + " receiver listener");
    WriteIndent("public protocol " + listener);
    if (p->import)
    {
        bool first = true;
        WriteIndent(": ");
        for (const auto& import : p->import->imports)
        {
            WriteIndent((first ? "" : ", ") + domain + ConvertPackageName(*import) + "." + listener);
            first = false;
        }
    }
    else
        WriteIndent(": " + domain + "Fbe.ReceiveLogListener");
    WriteIndent(" {");
    WriteLine();
    Indent(1);

    // Generate receiver listener handlers
    if (p->body)
    {
        for (const auto& s : p->body->structs)
        {
            if (s->message)
            {
                std::string struct_name = domain + package + "." + *s->name;
                WriteLineIndent("func onReceive(value: " + struct_name + ")");
            }
        }
    }

    // Generate receiver listener end
    Indent(-1);
    WriteLineIndent("}");
    WriteLine();

    WriteLineIndent("public extension " + listener + " {");
    Indent(1);
    // Generate default receiver listener handlers
    if (p->body)
    {
        for (const auto& s : p->body->structs)
        {
            if (s->message)
            {
                std::string struct_name = domain + package + "." + *s->name;
                WriteLineIndent("func onReceive(value: " + struct_name + ") {}");
            }
        }
    }
    Indent(-1);
    WriteLineIndent("}");

    // Generate footer
    GenerateFooter();

    // Store the file
    WriteEnd();
    Store(file);
}

void GeneratorSwift::GenerateProxy(const std::shared_ptr<Package>& p, bool final)
{
    std::string domain = ConvertDomain(p);
    std::string package = ConvertPackage(p);

    CppCommon::Path path = (CppCommon::Path(_output) / CreatePackagePath(domain, package)) / "Fbe";

    // Create package path
    CppCommon::Directory::CreateTree(path);

    std::string listener = (final ? "FinalProxyListener" : "ProxyListener");
    std::string proxy = (final ? "FinalProxy" : "Proxy");
    std::string model = (final ? "FinalModel" : "Model");

    // Generate the file
    CppCommon::Path file = path / (proxy + ".swift");
    WriteBegin();

    // Generate headers
    GenerateHeader(CppCommon::Path(_input).filename().string());
    GenerateImports("", "Foundation");
    GenerateImports(domain, "Fbe");
    GenerateImports(p);

    // Generate proxy begin
    WriteLine();
    if (final)
        WriteLineIndent("// Fast Binary Encoding " + package + " final proxy");
    else
        WriteLineIndent("// Fast Binary Encoding " + package + " proxy");
    WriteLineIndent("open class " + proxy + ": " + domain + "Fbe.ReceiverProtocol {");
    Indent(1);

    // Generate imported proxy accessors
    if (p->import)
    {
        WriteLineIndent("// Imported proxy");
        for (const auto& import : p->import->imports)
            WriteLineIndent("let " + ConvertPackageName(*import) + "Proxy: " + domain + ConvertPackageName(*import) + "." + proxy + "?");
        WriteLine();
    }

    // Generate proxy models accessors
    if (p->body)
    {
        WriteLineIndent("// Proxy models accessors");
        for (const auto& s : p->body->structs)
            if (s->message)
                WriteLineIndent("private let " + *s->name + "Model: " + *s->name + model);
        WriteLine();
    }

    WriteLineIndent("public var buffer: Buffer = Buffer()");
    WriteLineIndent("public var final: Bool = false");
    WriteLine();

    // Generate proxy constructors
    WriteLineIndent("public init() {");
    Indent(1);
    if (p->import)
        for (const auto& import : p->import->imports)
            WriteLineIndent(ConvertPackageName(*import) + "Proxy = " + domain + ConvertPackageName(*import) + "." + proxy + "(buffer: buffer)");
    if (p->body)
    {
        for (const auto& s : p->body->structs)
        {
            if (s->message)
            {
                std::string struct_name = domain + package + "." + *s->name;
                WriteLineIndent(*s->name + "Model = " + struct_name + model + "()");
            }
        }
    }
    WriteLineIndent("build(final: " + std::string(final ? "true" : "false") + ")");
    Indent(-1);
    WriteLineIndent("}");
    WriteLine();
    WriteLineIndent("public init(buffer: " + domain + "Fbe.Buffer) {");
    Indent(1);
    if (p->import)
        for (const auto& import : p->import->imports)
            WriteLineIndent(ConvertPackageName(*import) + "Proxy = " + domain + ConvertPackageName(*import) + "." + proxy + "(buffer: buffer)");
    if (p->body)
    {
        for (const auto& s : p->body->structs)
        {
            if (s->message)
            {
                std::string struct_name = domain + package + "." + *s->name;
                WriteLineIndent(*s->name + "Model = " + struct_name+ model + "()");
            }
        }
    }
    WriteLineIndent("build(with: buffer, final: " + std::string(final ? "true" : "false") + ")");
    Indent(-1);
    WriteLineIndent("}");
    WriteLine();

    bool messages = false;
    for (const auto& s : p->body->structs)
        if (s->message)
            messages = true;

    // Generate proxy message handler
    WriteLineIndent("open func onReceive(type: Int, buffer: Data, offset: Int, size: Int) -> Bool {");
    Indent(1);
    WriteLineIndent("guard let listener = self as? " + listener + " else { return false }");
    WriteLineIndent("return onReceiveListener(listener: listener, type: type, buffer: buffer, offset: offset, size: size)");
    Indent(-1);
    WriteLineIndent("}");
    WriteLine();
    WriteLineIndent("open func onReceiveListener(listener: " + listener + ", type: Int, buffer: Data, offset: Int, size: Int) -> Bool {");
    Indent(1);
    if (p->body && messages)
    {
        WriteLineIndent("switch type {");
        for (const auto& s : p->body->structs)
        {
            if (s->message)
            {
                WriteLineIndent("case " + domain + package + "." + *s->name + model + ".fbeTypeConst:");
                Indent(1);
                WriteLineIndent("// Attach the FBE stream to the proxy model");
                WriteLineIndent(*s->name + "Model.attach(buffer: buffer, offset: offset)");
                WriteLineIndent("assert(" + *s->name + "Model.verify(), \"" + package + "." + *s->name + " validation failed!\")");
                WriteLine();
                WriteLineIndent("let fbeBegin = " + *s->name + "Model.model.getBegin()");
                WriteLineIndent("if fbeBegin == 0 {");
                Indent(1);
                WriteLineIndent("return false");
                Indent(-1);
                WriteLineIndent("}");
                WriteLineIndent("// Call proxy handler");
                WriteLineIndent("listener.onProxy(model: " + *s->name + "Model, type: type, buffer: buffer, offset: offset, size: size)");
                WriteLineIndent(*s->name + "Model.model.getEnd(fbeBegin: fbeBegin)");
                WriteLineIndent("return true");
                Indent(-1);
            }
        }
        WriteLineIndent("default: break");
        WriteLineIndent("}");
    }
    if (p->import)
    {
        WriteLine();
        for (const auto& import : p->import->imports)
        {
            WriteLineIndent("if let " + ConvertPackageName(*import) + "Proxy = " + ConvertPackageName(*import) + "Proxy, " + ConvertPackageName(*import) + "Proxy.onReceiveListener(listener: listener, type: type, buffer: buffer, offset: offset, size: size) {");
            Indent(1);
            WriteLineIndent("return true");
            Indent(-1);
            WriteLineIndent("}");
        }
    }
    WriteLine();
    WriteLineIndent("return false");
    Indent(-1);
    WriteLineIndent("}");

    // Generate proxy end
    Indent(-1);
    WriteLineIndent("}");

    // Generate footer
    GenerateFooter();

    // Store the file
    WriteEnd();
    Store(file);
}

void GeneratorSwift::GenerateProxyListener(const std::shared_ptr<Package>& p, bool final)
{
    std::string domain = ConvertDomain(p);
    std::string package = ConvertPackage(p);

    CppCommon::Path path = (CppCommon::Path(_output) / CreatePackagePath(domain, package)) / "Fbe";

    // Create package path
    CppCommon::Directory::CreateTree(path);

    std::string listener = (final ? "FinalProxyListener" : "ProxyListener");
    std::string model = (final ? "FinalModel" : "Model");

    // Generate the file
    CppCommon::Path file = path / (listener + ".swift");
    WriteBegin();

    // Generate headers
    GenerateHeader(CppCommon::Path(_input).filename().string());
    GenerateImports(domain, "Fbe");
    GenerateImports("", "Foundation");
    GenerateImports(p);

    // Generate proxy listener begin
    WriteLine();
    if (final)
        WriteLineIndent("// Fast Binary Encoding " + domain + package + " final proxy listener");
    else
        WriteLineIndent("// Fast Binary Encoding " + domain + package + " proxy listener");
    WriteIndent("public protocol " + listener);
    if (p->import)
    {
        bool first = true;
        WriteIndent(": ");
        for (const auto& import : p->import->imports)
        {
            WriteIndent((first ? "" : ", ") + domain + ConvertPackageName(*import) + "." + listener);
            first = false;
        }
    }
    WriteIndent(" {");
    WriteLine();
    Indent(1);

    // Generate proxy listener handlers
    if (p->body)
    {
        for (const auto& s : p->body->structs)
        {
            if (s->message)
            {
                std::string struct_model = *s->name + model;
                WriteLineIndent("func onProxy(model: " + struct_model + ", type: Int, buffer: Data, offset: Int, size: Int)");
            }
        }
    }

    // Generate proxy listener end
    Indent(-1);
    WriteLineIndent("}");

    WriteLine();
    WriteLineIndent("public extension " + listener + " {");
    Indent(1);

    // Generate proxy listener handlers
    if (p->body)
    {
        for (const auto& s : p->body->structs)
        {
            std::string struct_model = *s->name + model;
            WriteLineIndent("func onProxy(model: " + struct_model + ", type: Int, buffer: Data, offset: Int, size: Int) {}");
        }
    }

    Indent(-1);
    WriteLineIndent("}");

    // Generate footer
    GenerateFooter();

    // Store the file
    WriteEnd();
    Store(file);
}

void GeneratorSwift::GenerateClient(const std::shared_ptr<Package>& p, bool final)
{
    std::string domain = ConvertDomain(p);
    std::string package = ConvertPackage(p);

    CppCommon::Path path = (CppCommon::Path(_output) / CreatePackagePath(domain, package)) / "Fbe";

    // Create package path
    CppCommon::Directory::CreateTree(path);

    std::string listener = (final ? "FinalReceiverListener" : "ReceiverListener");
    std::string client = (final ? "FinalClient" : "Client");
    std::string model = (final ? "FinalModel" : "Model");

    // Generate the file
    CppCommon::Path file = path / (client + ".swift");
    WriteBegin();

    // Generate headers
    GenerateHeader(CppCommon::Path(_input).filename().string());
    GenerateImports("", "Foundation");
    GenerateImports(domain, "Fbe");
    GenerateImports(p);

    // Generate client begin
    WriteLine();
    if (final)
        WriteLineIndent("// Fast Binary Encoding " + package + " final client");
    else
        WriteLineIndent("// Fast Binary Encoding " + package + " client");
    WriteLineIndent("open class " + client + ": " + domain + "Fbe.ClientProtocol {");
    Indent(1);

    // Generate imported clients accessors
    if (p->import)
    {
        WriteLineIndent("// Imported clients");
        for (const auto& import : p->import->imports)
            WriteLineIndent("let " + ConvertPackageName(*import) + "Client: " + domain + ConvertPackageName(*import) + "." + client);
        WriteLine();
    }

    // Generate client sender models accessors
    if (p->body)
    {
        WriteLineIndent("// Client sender models accessors");
        for (const auto& s : p->body->structs)
            if (s->message)
                WriteLineIndent("let " + *s->name + "SenderModel: " + *s->name + model);
        WriteLine();
    }

    // Generate client receiver models accessors
    if (p->body)
    {
        WriteLineIndent("// Client receiver values accessors");
        for (const auto& s : p->body->structs)
        {
            if (s->message)
            {
                std::string struct_name = domain + package + "." + *s->name;
                WriteLineIndent("private var " + *s->name + "ReceiverValue: " + struct_name);
            }
        }
        WriteLine();
        WriteLineIndent("// Client receiver models accessors");
        for (const auto& s : p->body->structs)
            if (s->message)
              WriteLineIndent("private let " + *s->name + "ReceiverModel: " + *s->name + model);
        WriteLine();
    }

    WriteLineIndent("public var sendBuffer: Buffer = Buffer()");
    WriteLineIndent("public var receiveBuffer: Buffer = Buffer()");
    WriteLineIndent("public var final: Bool = false");
    WriteLine();

    // Generate client constructors
    WriteLineIndent("public init() {");
    Indent(1);
    if (p->import)
        for (const auto& import : p->import->imports)
            WriteLineIndent(ConvertPackageName(*import) + "Client = " + domain + ConvertPackageName(*import) + "." + client + "(sendBuffer: sendBuffer, receiveBuffer: receiveBuffer)");
    if (p->body)
    {
        for (const auto& s : p->body->structs)
        {
            if (s->message)
            {
                std::string struct_name = domain + package + "." + *s->name;
                WriteLineIndent(*s->name + "SenderModel = " + *s->name + model + "(buffer: sendBuffer)");
                WriteLineIndent(*s->name + "ReceiverValue = " + struct_name + "()");
                WriteLineIndent(*s->name + "ReceiverModel = " + *s->name + model + "()");
            }

        }
    }
    WriteLineIndent("build(with: " + std::string(final ? "true" : "false") + ")");
    Indent(-1);
    WriteLineIndent("}");
    WriteLine();
    WriteLineIndent("public init(sendBuffer: " + domain + "Fbe.Buffer, receiveBuffer: " + domain + "Fbe.Buffer) {");
    Indent(1);
    if (p->import)
        for (const auto& import : p->import->imports)
            WriteLineIndent(ConvertPackageName(*import) + "Client = " + domain + ConvertPackageName(*import) + "." + client + "(sendBuffer: sendBuffer, receiveBuffer: receiveBuffer)");
    if (p->body)
    {
        for (const auto& s : p->body->structs)
        {
            if (s->message)
            {
                std::string struct_name = domain + package + "." + *s->name;
                WriteLineIndent(*s->name + "SenderModel = " + *s->name + model + "(buffer: sendBuffer)");
                WriteLineIndent(*s->name + "ReceiverValue = " + struct_name + "()");
                WriteLineIndent(*s->name + "ReceiverModel = " + *s->name + model + "()");
            }

        }
    }
    WriteLineIndent("build(with: sendBuffer, receiveBuffer: receiveBuffer, final: " + std::string(final ? "true" : "false") + ")");
    Indent(-1);
    WriteLineIndent("}");
    WriteLine();

    WriteLineIndent("public func send(obj: Any) throws -> Int {");
    Indent(1);
    WriteLineIndent("guard let listener = self as? " + domain + "Fbe.SenderListener else { return 0 }");
    WriteLineIndent("return try send(obj: obj, listener: listener)");
    Indent(-1);
    WriteLineIndent("}");
    WriteLine();

    bool messages = false;
    for (const auto& s : p->body->structs)
        if (s->message)
            messages = true;

    // Generate generic client send method
    WriteLineIndent("public func send(obj: Any, listener: " + domain + "Fbe.SenderListener) throws -> Int {");
    Indent(1);
    if (p->body && messages)
    {
        WriteLineIndent("let objType = type(of: obj)");
        for (const auto& s : p->body->structs)
        {
            if (s->message)
            {
                std::string struct_name = domain + package + "." + *s->name;
                WriteLineIndent("if objType == " + struct_name + ".self, let value = obj as? " + struct_name + " { return try send(value: value, listener: listener) }");
            }
        }
    }
    WriteLine();
    if (p->import)
    {
        WriteLineIndent("// Try to send using imported clients");
        WriteLineIndent("var result: Int = 0");
        for (const auto& import : p->import->imports)
        {
            WriteLineIndent("result = try " + ConvertPackageName(*import) + "Client.send(obj: obj, listener: listener)");
            WriteLineIndent("if result > 0 { return result }");
        }
        WriteLine();
    }
    WriteLineIndent("return 0");
    Indent(-1);
    WriteLineIndent("}");

    // Generate client send methods
    if (p->body)
    {
        for (const auto& s : p->body->structs)
        {
            if (s->message)
            {
                std::string struct_name = domain + package + "." + *s->name;
                WriteLine();
                WriteLineIndent("public func send(value: " + struct_name + ") throws -> Int {");
                Indent(1);
                WriteLineIndent("guard let listener = self as? " + domain + "Fbe.SenderListener else { return 0 }");
                WriteLineIndent("return try send(value: value, listener: listener)");
                Indent(-1);
                WriteLineIndent("}");

                WriteLine();
                WriteLineIndent("public func send(value: " + struct_name + ", listener: " + domain + "Fbe.SenderListener) throws -> Int {");
                Indent(1);
                WriteLineIndent("// Serialize the value into the FBE stream");
                WriteLineIndent("let serialized = try " + *s->name + "SenderModel.serialize(value: value)");
                WriteLineIndent("assert(serialized > 0, \"" + struct_name + " serialization failed!\")");
                WriteLineIndent("assert(" + *s->name + "SenderModel.verify(), \"" + struct_name + " validation failed!\")");
                WriteLine();
                WriteLineIndent("// Log the value");
                WriteLineIndent("if listener.logging {");
                Indent(1);
                WriteLineIndent("let message = value.description");
                WriteLineIndent("listener.onSendLog(message: message)");
                Indent(-1);
                WriteLineIndent("}");
                WriteLine();
                WriteLineIndent("// Send the serialized value");
                WriteLineIndent("return try sendSerialized(listener: listener, serialized: serialized)");
                Indent(-1);
                WriteLineIndent("}");
            }
        }
    }

    // Generate client receive message handler
    WriteLineIndent("open func onReceive(type: Int, buffer: Data, offset: Int, size: Int) -> Bool {");
    Indent(1);
    WriteLineIndent("guard let listener = self as? " + listener + " else { return false }");
    WriteLineIndent("return onReceiveListener(listener: listener, type: type, buffer: buffer, offset: offset, size: size)");
    Indent(-1);
    WriteLineIndent("}");
    WriteLine();
    WriteLineIndent("open func onReceiveListener(listener: " + listener + ", type: Int, buffer: Data, offset: Int, size: Int) -> Bool {");
    Indent(1);
    if (p->body)
    {
        WriteLineIndent("switch type {");
        for (const auto& s : p->body->structs)
        {
            if (s->message)
            {
                WriteLineIndent("case " + domain + package + "." + *s->name + model + ".fbeTypeConst:");
                Indent(1);
                WriteLineIndent("// Deserialize the value from the FBE stream");
                WriteLineIndent(*s->name + "ReceiverModel.attach(buffer: buffer, offset: offset)");
                WriteLineIndent("assert(" + *s->name + "ReceiverModel.verify(), \"" + package + "." + *s->name + " validation failed!\")");
                WriteLineIndent("let deserialized = " + *s->name + "ReceiverModel.deserialize(value: &" + *s->name + "ReceiverValue)");
                WriteLineIndent("assert(deserialized > 0, \"" + package + "." + *s->name + " deserialization failed!\")");
                WriteLine();
                WriteLineIndent("// Log the value");
                WriteLineIndent("if listener.logging {");
                Indent(1);
                WriteLineIndent("let message = " + *s->name + "ReceiverValue.description");
                WriteLineIndent("listener.onReceiveLog(message: message)");
                Indent(-1);
                WriteLineIndent("}");
                WriteLine();
                WriteLineIndent("// Call receive handler with deserialized value");
                WriteLineIndent("listener.onReceive(value: " + *s->name + "ReceiverValue)");
                WriteLineIndent("return true");
                Indent(-1);
            }
        }
        WriteLineIndent("default: break");
        WriteLineIndent("}");
    }
    if (p->import)
    {
        WriteLine();
        for (const auto& import : p->import->imports)
        {
            WriteLineIndent("if " + ConvertPackageName(*import) + "Client.onReceiveListener(listener: listener, type: type, buffer: buffer, offset: offset, size: size) {");
            Indent(1);
            WriteLineIndent("return true");
            Indent(-1);
            WriteLineIndent("}");
        }
    }
    WriteLine();
    WriteLineIndent("return false");
    Indent(-1);
    WriteLineIndent("}");

    // Generate client end
    Indent(-1);
    WriteLineIndent("}");

    // Generate footer
    GenerateFooter();

    // Store the file
    WriteEnd();
    Store(file);
}

bool GeneratorSwift::IsKnownType(const std::string& type)
{
    return ((type == "bool") ||
            (type == "byte") || (type == "bytes") ||
            (type == "char") || (type == "wchar") ||
            (type == "int8") || (type == "uint8") ||
            (type == "int16") || (type == "uint16") ||
            (type == "int32") || (type == "uint32") ||
            (type == "int64") || (type == "uint64") ||
            (type == "float") || (type == "double") ||
            (type == "decimal") || (type == "string") ||
            (type == "timestamp") || (type == "uuid"));
}

bool GeneratorSwift::IsImportedType(const std::string& type)
{
    size_t pos = type.find_last_of('.');
    return (pos != std::string::npos);
}

bool GeneratorSwift::IsPackageType(const std::string& type)
{
    if (IsKnownType(type) || IsImportedType(type))
        return true;

    return ((type == "Bool") || (type == "Bool?") ||
            (type == "UInt8") || (type == "UInt8?") || (type == "Data") || (type == "Data?") ||
            (type == "Character") || (type == "Character?") ||
            (type == "Int8") || (type == "Int8?") || (type == "UInt8") || (type == "UInt8?") ||
            (type == "Int16") || (type == "Int16?") || (type == "UInt16") || (type == "UInt16?") ||
            (type == "Int32") || (type == "Int32?") || (type == "UInt32") || (type == "UInt32?") ||
            (type == "Int64") || (type == "Int64?") || (type == "UInt64") || (type == "UInt64?") ||
            (type == "Float") || (type == "Float?") || (type == "Double") || (type == "Double?") ||
            (type == "Decimal") || (type == "Decimal?") ||
            (type == "String") || (type == "String?") || (type == "Date") || (type == "Date?") || (type == "UUID") || (type == "UUID?"));
}

bool GeneratorSwift::IsPrimitiveType(const std::string& type, bool optional)
{
    return ((type == "bool") || (type == "byte") ||
            (type == "char") || (type == "wchar") ||
            (type == "int8") || (type == "uint8") ||
            (type == "int16") || (type == "uint16") ||
            (type == "int32") || (type == "uint32") ||
            (type == "int64") || (type == "uint64") ||
            (type == "float") || (type == "double") ||
            (type == "string"));
}

bool GeneratorSwift::IsUnsignedType(const std::string& type)
{
    return ((type == "byte") || (type == "uint8") || (type == "uint16") || (type == "uint32") || (type == "uint64"));
}

CppCommon::Path GeneratorSwift::CreatePackagePath(const std::string& domain, const std::string& package)
{
    return CppCommon::Path(package) / "Source" / (domain + package);
}

std::string GeneratorSwift::ConvertEnumBase(const std::string& type)
{
    if (type == "byte")
        return "UInt8";
    else if (type == "char")
        return "UInt8";
    else if (type == "wchar")
        return "UInt32";
    else if (type == "int8")
        return "Int8";
    else if (type == "uint8")
        return "UInt8";
    else if (type == "int16")
        return "Int16";
    else if (type == "uint16")
        return "UInt16";
    else if (type == "int32")
        return "Int32";
    else if (type == "uint32")
        return "UInt32";
    else if (type == "int64")
        return "Int64";
    else if (type == "uint64")
        return "UInt64";

    yyerror("Unsupported enum base type - " + type);
    return "";
}

std::string GeneratorSwift::ConvertEnumType(const std::string& type)
{
    if (type == "byte")
        return "UInt8";
    else if (type == "char")
        return "UInt8";
    else if (type == "wchar")
        return "UInt32";
    else if (type == "int8")
        return "Int8";
    else if (type == "uint8")
        return "UInt8";
    else if (type == "int16")
        return "Int16";
    else if (type == "uint16")
        return "UInt16";
    else if (type == "int32")
        return "Int32";
    else if (type == "uint32")
        return "UInt32";
    else if (type == "int64")
        return "Int64";
    else if (type == "uint64")
        return "UInt64";

    yyerror("Unsupported enum base type - " + type);
    return "";
}

std::string GeneratorSwift::ConvertEnumSize(const std::string& type)
{
    if (type == "byte")
        return "1";
    else if (type == "char")
        return "1";
    else if (type == "wchar")
        return "4";
    else if (type == "int8")
        return "1";
    else if (type == "uint8")
        return "1";
    else if (type == "int16")
        return "2";
    else if (type == "uint16")
        return "2";
    else if (type == "int32")
        return "4";
    else if (type == "uint32")
        return "4";
    else if (type == "int64")
        return "8";
    else if (type == "uint64")
        return "8";

    yyerror("Unsupported enum base type - " + type);
    return "";
}

std::string GeneratorSwift::ConvertEnumGet(const std::string& type)
{
    if (type == "byte")
        return "asByte";
    else if (type == "char")
        return "asByte";
    else if (type == "wchar")
        return "asInt";
    else if (type == "int8")
        return "asByte";
    else if (type == "uint8")
        return "asByte.toUByte()";
    else if (type == "int16")
        return "asShort";
    else if (type == "uint16")
        return "asShort.toUShort()";
    else if (type == "int32")
        return "asInt";
    else if (type == "uint32")
        return "asInt.toUInt()";
    else if (type == "int64")
        return "asLong";
    else if (type == "uint64")
        return "asLong.toULong()";

    yyerror("Unsupported enum base type - " + type);
    return "";
}

std::string GeneratorSwift::ConvertEnumRead(const std::string& type)
{
    if (type == "byte")
        return "readByte";
    else if (type == "char")
        return "readUInt8";
    else if (type == "wchar")
        return "readUInt32";
    else if (type == "int8")
        return "readInt8";
    else if (type == "uint8")
        return "readUInt8";
    else if (type == "int16")
        return "readInt16";
    else if (type == "uint16")
        return "readUInt16";
    else if (type == "int32")
        return "readInt32";
    else if (type == "uint32")
        return "readUInt32";
    else if (type == "int64")
        return "readInt64";
    else if (type == "uint64")
        return "readUInt64";

    yyerror("Unsupported enum base type - " + type);
    return "";
}

std::string GeneratorSwift::ConvertEnumFrom(const std::string& type)
{
    if (type == "uint8")
        return ".toByte()";
    else if (type == "uint16")
        return ".toShort()";
    else if (type == "uint32")
        return ".toInt()";
    else if (type == "uint64")
        return ".toLong()";

    return "";
}

std::string GeneratorSwift::ConvertEnumTo(const std::string& type)
{
    if (type == "byte")
        return ".uint8Value";
    else if (type == "char")
        return ".uint8Value";
    else if (type == "wchar")
        return ".uint32Value";
    else if (type == "int8")
        return ".int8Value";
    else if (type == "uint8")
        return ".uint8Value";
    else if (type == "int16")
        return ".int16Value";
    else if (type == "uint16")
        return ".uint16Value";
    else if (type == "int32")
        return ".int32Value";
    else if (type == "uint32")
        return ".uint32Value";
    else if (type == "int64")
        return ".int64Value";
    else if (type == "uint64")
        return ".uint64Value";

    yyerror("Unsupported enum base type - " + type);
    return "";
}

std::string GeneratorSwift::ConvertEnumFlags(const std::string& type)
{
    if (type == "byte")
        return ".intValue";
    else if (type == "int8")
        return ".intValue";
    else if (type == "uint8")
        return ".uintValue";
    else if (type == "int16")
        return ".intValue";
    else if (type == "uint16")
        return ".uintValue";
    else if (type == "int32")
        return ".intValue";
    else if (type == "uint32")
        return ".uintValue";
    else if (type == "int64")
        return ".int64Value";
    else if (type == "uint64")
        return ".uint64Value";

    yyerror("Unsupported enum base type - " + type);
    return "";
}

std::string GeneratorSwift::ConvertEnumConstant(const std::string& base, const std::string& type, const std::string& value, bool flag)
{
    std::string result = value;

    if (type.empty())
    {
        // Fill flags values
        std::vector<std::string> flags = CppCommon::StringUtils::Split(value, '|', true);

        // Generate flags combination
        if (!flags.empty())
        {
            result = "";
            bool first = true;
            for (const auto& it : flags)
            {
                result += std::string(first ? "" : " | ") + "Self." + CppCommon::StringUtils::ToTrim(it) + ".rawValue";
                first = false;
            }
        }
    }

    result = std::regex_replace(result, std::regex("'"), "\"");
    return ConvertEnumConstantPrefix(type) + result + ConvertEnumConstantSuffix(type);
}

std::string GeneratorSwift::ConvertEnumConstantPrefix(const std::string& type)
{
    return "";
}

std::string GeneratorSwift::ConvertEnumConstantSuffix(const std::string& type)
{
    if ((type == "char"))
        return ".utf8.map { UInt8($0) }[0]";
    if ((type == "uint8") || (type == "uint16") || (type == "uint32"))
        return "";
    if (type == "int64")
        return "";
    if (type == "uint64")
        return "";

    return "";
}

std::string GeneratorSwift::ConvertPrimitiveTypeName(const std::string& type)
{
    if (type == "bool")
        return "Bool";
    else if (type == "byte")
        return "UInt8";
    else if (type == "char")
        return "Character";
    else if (type == "wchar")
        return "Character";
    else if (type == "int8")
        return "Int8";
    else if (type == "uint8")
        return "UInt8";
    else if (type == "int16")
        return "Int16";
    else if (type == "uint16")
        return "UInt16";
    else if (type == "int32")
        return "Int32";
    else if (type == "uint32")
        return "UInt32";
    else if (type == "int64")
        return "Int64";
    else if (type == "uint64")
        return "UInt64";
    else if (type == "float")
        return "Float";
    else if (type == "double")
        return "Double";

    return "";
}

std::string GeneratorSwift::ConvertTypeName(const std::string& domain, const std::string& package, const std::string& type, bool optional)
{
    std::string opt = optional ? "?" : "";

    if (type == "bool")
        return "Bool" + opt;
    else if (type == "byte")
        return "UInt8" + opt;
    else if (type == "bytes")
        return "Data" + opt;
    else if (type == "char")
        return "Character" + opt;
    else if (type == "wchar")
        return "Character" + opt;
    else if (type == "int8")
        return "Int8" + opt;
    else if (type == "uint8")
        return "UInt8" + opt;
    else if (type == "int16")
        return "Int16" + opt;
    else if (type == "uint16")
        return "UInt16" + opt;
    else if (type == "int32")
        return "Int32" + opt;
    else if (type == "uint32")
        return "UInt32" + opt;
    else if (type == "int64")
        return "Int64" + opt;
    else if (type == "uint64")
        return "UInt64" + opt;
    else if (type == "float")
        return "Float" + opt;
    else if (type == "double")
        return "Double" + opt;
    else if (type == "decimal")
        return "Decimal" + opt;
    else if (type == "string")
        return "String" + opt;
    else if (type == "timestamp")
        return "Date" + opt;
    else if (type == "uuid")
        return "UUID" + opt;

    std::string ns = "";
    std::string t = type;

    size_t pos = type.find_last_of('.');
    if (pos != std::string::npos)
    {
        ns.assign(type, 0, pos + 1);
        ns[0] = toupper(ns[0]);
        ns = domain + ns;
        t.assign(type, pos + 1, type.size() - pos);
    }
    else if (!package.empty())
        ns = domain + package + ".";

    return ns + t + opt;
}

std::string GeneratorSwift::ConvertTypeName(const std::string& domain, const std::string& package, const StructField& field, bool typeless)
{
    if (field.array)
        return "Array" + (typeless ? "" : ("<" + ConvertTypeName(domain, package, *field.type, field.optional) + ">"));
    else if (field.vector)
        return "Array" + (typeless ? "" : ("<" + ConvertTypeName(domain, package, *field.type, field.optional) + ">"));
    else if (field.list)
        return "Array" + (typeless ? "" : ("<" + ConvertTypeName(domain, package, *field.type, field.optional) + ">"));
    else if (field.set)
        return "Array" + (typeless ? "" : ("<" + ConvertTypeName(domain, package, *field.key, field.optional) + ">"));
    else if (field.map)
        return "Dictionary" + (typeless ? "" : ("<" + ConvertTypeName(domain, package, *field.key, false) + ", " + ConvertTypeName(domain, package, *field.type, field.optional) +">"));
    else if (field.hash)
        return "Dictionary" + (typeless ? "" : ("<" + ConvertTypeName(domain, package, *field.key, false) + ", " + ConvertTypeName(domain, package, *field.type, field.optional) +">"));

    return ConvertTypeName(domain, package, *field.type, field.optional);
}

std::string GeneratorSwift::ConvertTypeImport(const std::string& type)
{
    std::string ns = "";

    size_t pos = type.find_last_of('.');
    if (pos != std::string::npos)
        ns.assign(type, 0, pos);
    else if (!type.empty())
        ns = "";

    return ns;
}

std::string GeneratorSwift::ConvertBaseFieldName(const std::string& domain, const std::string& type, bool final)
{
    std::string modelType = final ? "Final" : "Field";

    std::string ns = "";
    std::string t = type;

    size_t pos = type.find_last_of('.');
    if (pos != std::string::npos)
    {
        ns.assign(type, 0, pos + 1);
        ns[0] = toupper(ns[0]);
        ns = domain + ns;
        t.assign(type, pos + 1, type.size() - pos);
    }

    return ns + modelType + "Model" + ConvertTypeFieldName(t);
}

std::string GeneratorSwift::ConvertTypeFieldName(const std::string& type)
{
    if (type == "bool")
        return "Boolean";
    else if (type == "byte")
        return "UInt8";
    else if (type == "bytes")
        return "Data";
    else if (type == "char")
        return "Char";
    else if (type == "wchar")
        return "WChar";
    else if (type == "int8")
        return "Int8";
    else if (type == "uint8")
        return "UInt8";
    else if (type == "int16")
        return "Int16";
    else if (type == "uint16")
        return "UInt16";
    else if (type == "int32")
        return "Int32";
    else if (type == "uint32")
        return "UInt32";
    else if (type == "int64")
        return "Int64";
    else if (type == "uint64")
        return "UInt64";
    else if (type == "float")
        return "Float";
    else if (type == "double")
        return "Double";
    else if (type == "decimal")
        return "Decimal";
    else if (type == "string")
        return "String";
    else if (type == "timestamp")
        return "Timestamp";
    else if (type == "uuid")
        return "UUID";

    std::string result = type;
    size_t pos = type.find_last_of('.');
    if (pos != std::string::npos)
        result.assign(type, pos + 1, type.size() - pos);

    CppCommon::StringUtils::ReplaceAll(result, ".", "");
    return result;
}

std::string GeneratorSwift::ConvertTypeFieldType(const std::string& domain, const std::string& type, bool optional)
{
    std::string opt = optional ? "?" : "";

    if (type == "bool")
        return "Bool" + opt;
    else if (type == "byte")
        return "UInt8" + opt;
    else if (type == "bytes")
        return "Data" + opt;
    else if (type == "char")
        return "Character" + opt;
    else if (type == "wchar")
        return "Character" + opt;
    else if (type == "int8")
        return "Int8" + opt;
    else if (type == "uint8")
        return "UInt8" + opt;
    else if (type == "int16")
        return "Int16" + opt;
    else if (type == "uint16")
        return "UInt16" + opt;
    else if (type == "int32")
        return "Int32" + opt;
    else if (type == "uint32")
        return "UInt32" + opt;
    else if (type == "int64")
        return "Int64" + opt;
    else if (type == "uint64")
        return "UInt64" + opt;
    else if (type == "float")
        return "Float" + opt;
    else if (type == "double")
        return "Double" + opt;
    else if (type == "decimal")
        return "Decimal" + opt;
    else if (type == "string")
        return "String" + opt;
    else if (type == "timestamp")
        return "Date" + opt;
    else if (type == "uuid")
        return "UUID" + opt;

    std::string ns = "";
    std::string t = type;

    size_t pos = type.find_last_of('.');
    if (pos != std::string::npos)
    {
        ns.assign(type, 0, pos + 1);
        ns[0] = toupper(ns[0]);
        ns = domain + ns;
        t.assign(type, pos + 1, type.size() - pos);
    }

    return ns + t + opt;
}

std::string GeneratorSwift::ConvertTypeFieldDeclaration(const std::string& domain, const std::string& type, bool optional, bool final)
{
    std::string modelType = final ? "Final" : "Field";

    std::string ns = "";
    std::string opt = optional ? "Optional" : "";
    std::string t = type;

    size_t pos = type.find_last_of('.');
    if (pos != std::string::npos)
    {
        ns.assign(type, 0, pos + 1);
        ns[0] = toupper(ns[0]);
        ns = domain + ns;
        t.assign(type, pos + 1, type.size() - pos);
    }
    else
        ns = (IsKnownType(type) && !optional) ? (domain + "Fbe.") : "";

    return ns + modelType + "Model" + opt + ConvertTypeFieldName(t);
}

std::string GeneratorSwift::ConvertTypeFieldDeclaration(const std::string& domain, const StructField& field, bool final)
{
    std::string modelType = final ? "Final" : "Field";

    if (field.array)
        return modelType + "ModelArray" + std::string(field.optional ? "Optional" : "") + ConvertTypeFieldName(*field.type);
    else if (field.vector || field.list || field.set)
        return modelType + "ModelVector" + std::string(field.optional ? "Optional" : "") + ConvertTypeFieldName(*field.type);
    else if (field.map || field.hash)
        return modelType + "ModelMap" + ConvertTypeFieldName(*field.key) + std::string(field.optional ? "Optional" : "") + ConvertTypeFieldName(*field.type);
    else if (field.optional)
        return modelType + "ModelOptional" + ConvertTypeFieldName(*field.type);

    return ConvertTypeFieldDeclaration(domain, *field.type, field.optional, final);
}

std::string GeneratorSwift::ConvertTypeFieldInitialization(const std::string& domain, const StructField& field, const std::string& offset, bool final)
{
    std::string modelType = final ? "Final" : "Field";

    if (field.array)
        return modelType + "ModelArray" + std::string(field.optional ? "Optional" : "") + ConvertTypeFieldName(*field.type) + "(buffer: buffer, offset: " + offset + ", size: " + std::to_string(field.N) + ")";
    else if (field.vector || field.list || field.set)
        return modelType + "ModelVector" + std::string(field.optional ? "Optional" : "") + ConvertTypeFieldName(*field.type) + "(buffer: buffer, offset: " + offset + ")";
    else if (field.map || field.hash)
        return modelType + "ModelMap" + ConvertTypeFieldName(*field.key) + std::string(field.optional ? "Optional" : "") + ConvertTypeFieldName(*field.type) + "(buffer: buffer, offset: " + offset + ")";
    else if (field.optional)
        return modelType + "ModelOptional" + ConvertTypeFieldName(*field.type) + "(buffer: buffer, offset: " + offset + ")";

    std::string ns = "";
    std::string t = *field.type;
    std::string type = *field.type;

    size_t pos = type.find_last_of('.');
    if (pos != std::string::npos)
    {
        ns.assign(type, 0, pos + 1);
        ns[0] = toupper(ns[0]);
        ns = domain + ns;
        t.assign(type, pos + 1, type.size() - pos);
    }
    else
        ns = "";

    return ns + modelType + "Model" + ConvertTypeFieldName(t) + "(buffer: buffer, offset: " + offset + ")";
}

std::string GeneratorSwift::ConvertConstant(const std::string& domain, const std::string& package, const std::string& type, const std::string& value, bool optional)
{
    if (value == "true")
        return "true";
    else if (value == "false")
        return "false";
    else if (value == "null")
        return "nil";
    else if (value == "min")
    {
        if (type == "byte")
            return ConvertConstantPrefix(type) + "0" + ConvertConstantSuffix(type);
        else if (type == "int8")
            return "Int8.min";
        else if (type == "uint8")
            return "UInt8.min";
        else if (type == "int16")
            return "Int16.min";
        else if (type == "uint16")
            return "UInt16.min";
        else if (type == "int32")
            return "Int32.min";
        else if (type == "uint32")
            return "UInt32.min";
        else if (type == "int64")
            return "Int64.min";
        else if (type == "uint64")
            return "UInt64.min";

        yyerror("Unsupported type " + type + " for 'min' constant");
        return "";
    }
    else if (value == "max")
    {
        if (type == "byte")
            return ConvertConstantPrefix(type) + "0xFF" + ConvertConstantSuffix(type);
        else if (type == "int8")
            return "Int8.max";
        else if (type == "uint8")
            return "UInt8.max";
        else if (type == "int16")
            return "Int16.max";
        else if (type == "uint16")
            return "UInt16.max";
        else if (type == "int32")
            return "Int32.max";
        else if (type == "uint32")
            return "UInt32.max";
        else if (type == "int64")
            return "Int64.max";
        else if (type == "uint64")
            return "UInt64.max";

        yyerror("Unsupported type " + type + " for 'max' constant");
        return "";
    }
    else if (value == "epoch")
        return "Date(timeIntervalSince1970: 0)";
    else if (value == "utc")
        return "Date()";
    else if (value == "uuid0")
        return domain + "Fbe.UUIDGenerator.nil()";
    else if (value == "uuid1")
        return domain +  "Fbe.UUIDGenerator.sequential()";
    else if (value == "uuid4")
        return domain + "Fbe.UUIDGenerator.random()";

    std::string result = value;
    result[0] = toupper(result[0]);

    if (!IsKnownType(type))
    {
        // Fill flags values
        std::vector<std::string> flags = CppCommon::StringUtils::Split(value, '|', true);

        // Generate flags combination
        if (flags.size() > 1)
        {
            result = "";
            bool first = true;
            for (const auto& it : flags)
            {
                std::string flag = CppCommon::StringUtils::ToTrim(it);
                result += (first ? "" : ", ") + flag + ".value!";
                first = false;
            }
            result = type + ".fromSet(set: [" + result + "])";
        }
        else
        {
            size_t pos = result.find_last_of('.');
            if (pos != std::string::npos)
            {
                if (CppCommon::StringUtils::CountAll(result, ".") > 1)
                    result = domain + result;
                else
                    result = (package.empty() ? "" : (domain + package + ".")) + result;
            }
        }
    }

    result = std::regex_replace(result, std::regex("'"), "\"");
    return ConvertConstantPrefix(type) + result + ConvertConstantSuffix(type);
}

std::string GeneratorSwift::ConvertConstantPrefix(const std::string& type)
{
    if (type == "decimal")
        return "Decimal(string: \"";
    if (type == "uuid")
        return "UUID(uuidString: ";
    if (type == "wchar")
        return "Character(UnicodeScalar(";

    return "";
}

std::string GeneratorSwift::ConvertConstantSuffix(const std::string& type)
{
    if (type == "byte")
        return "";
    if ((type == "uint8") || (type == "uint16") || (type == "uint32"))
        return "";
    if (type == "int64")
        return "";
    if (type == "uint64")
        return "";
    if (type == "float")
        return "";

    if (type == "wchar")
        return ")!)";
    if (type == "uuid")
        return ")!";
    if (type == "decimal")
        return "\")!";

    return "";
}

std::string GeneratorSwift::ConvertDefault(const std::string& domain, const std::string& package, const std::string& type)
{
    if (type == "bool")
        return "false";
    else if (type == "byte")
        return "0";
    else if (type == "bytes")
        return "Data()";
    else if (type == "char")
        return "\"\\0\"";
    else if (type == "wchar")
        return "Character(UnicodeScalar(0)!)";
    else if (type == "int8")
        return "0";
    else if (type == "uint8")
        return "0";
    else if (type == "int16")
        return "0";
    else if (type == "uint16")
        return "0";
    else if (type == "int32")
        return "0";
    else if (type == "uint32")
        return "0";
    else if (type == "int64")
        return "0";
    else if (type == "uint64")
        return "0";
    else if (type == "float")
        return "0.0";
    else if (type == "double")
        return "0.0";
    else if (type == "decimal")
        return "Decimal.zero";
    else if (type == "string")
        return "\"\"";
    else if (type == "timestamp")
        return "Date(timeIntervalSince1970: 0)";
    else if (type == "uuid")
        return domain + "Fbe.UUIDGenerator.nil()";

    std::string ns = "";
    std::string t = type;

    size_t pos = type.find_last_of('.');
    if (pos != std::string::npos)
    {
        ns.assign(type, 0, pos + 1);
        ns[0] = toupper(ns[0]);
        ns = domain + ns;
        t.assign(type, pos + 1, type.size() - pos);
    }
    else if (!package.empty())
        ns = domain + package + ".";

    return ns + t + "()";
}

std::string GeneratorSwift::ConvertDefault(const std::string& domain, const std::string& package, const StructField& field)
{
    if (field.value)
        return ConvertConstant(domain, package, *field.type, *field.value, field.optional);

    if (field.array)
        return "Array()";
    else if (field.vector || field.list || field.set || field.map || field.hash)
        return ConvertTypeName(domain, package, field, true) + "()";
    else if (field.optional)
        return "nil";
    else if (!IsPackageType(*field.type))
        return ConvertTypeName(domain, package, field, true) + "()";

    return ConvertDefault(domain, package, *field.type);
}

std::string GeneratorSwift::ConvertOutputStreamType(const std::string& type, const std::string& name, bool optional)
{
    std::string opt = optional ? "!" : "";

    if (type == "bool")
        return ".append(" + name + opt + " ? \"true\" : \"false\")";
    else if (type == "bytes")
        return ".append(\"bytes[\"); sb.append(\"\\(" + name + opt + ".count)\"); sb.append(\"]\")";
    else if ((type == "char") || (type == "wchar"))
        return ".append(\"'\"); sb.append(" + name + opt + "); sb.append(\"'\")";
    else if (type == "string")
        return ".append(\"\\\"\"); sb.append(" + name + opt + "); sb.append(\"\\\"\")";
    else if (type == "uuid")
        return ".append(\"\\\"\"); sb.append(" + name + opt + ".uuidString); sb.append(\"\\\"\")";
    else if (type == "timestamp")
        return ".append(\"\\(floor(" + name + opt + ".timeIntervalSince1970 * 1000) * 1_000_000)\")";
    else
        return ".append(" + name + opt + ".description)";
}

std::string GeneratorSwift::ConvertOutputStreamValue(const std::string& type, const std::string& name, bool optional, bool separate, bool nullable)
{
    std::string comma = separate ? ".append(first ? \"\" : \",\"); sb" : "";

    if (optional)
        return " if let " + name + " = " + name + " { sb" + comma + ConvertOutputStreamType(type, name, false) + " } else { sb" + comma + ".append(\"null\") }";
    else
        return "sb" + comma + ConvertOutputStreamType(type, name, false);
}

std::string GeneratorSwift::ConvertDomain(const std::shared_ptr<Package>& package)
{
    std::string domain = (package->domain && !package->domain->empty()) ? (*package->domain + ".") : "";
    CppCommon::StringUtils::ReplaceAll(domain, "com.", "");
    domain[0] = toupper(domain[0]);
    size_t pos = domain.find_last_of('.');
    while(pos != std::string::npos)
    {
        domain[pos + 1] = toupper(domain[pos + 1]);
        domain.erase(pos, 1);
        pos = domain.find_last_of('.');
    }
    return domain;
}

std::string GeneratorSwift::ConvertPackage(const std::shared_ptr<Package>& package)
{
    std::string packageName = *package->name;
    packageName[0] = toupper(packageName[0]);
    return packageName;
}

std::string GeneratorSwift::ConvertPackageName(const std::string& package)
{
    std::string packageName = package;
    packageName[0] = toupper(packageName[0]);
    return packageName;
}

} // namespace FBE
